Skip to content

Commit

Permalink
map provider error
Browse files Browse the repository at this point in the history
  • Loading branch information
kariy committed Apr 14, 2024
1 parent f064fc2 commit 0634b18
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 46 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ thiserror = "1.0.56"
[profile.dev.package.backtrace]
opt-level = 3

[profile.release]
[profile.maxperf]
codegen-units = 1
incremental = false
inherits = "release"
lto = "fat"
34 changes: 23 additions & 11 deletions crates/ops/src/rpc/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use starknet::{
types::{BlockId, FieldElement, StarknetError},
utils::parse_cairo_short_string,
},
macros::selector,
providers::{Provider, ProviderError},
};

use super::call::{contract_call, ContractCallError};
use super::call::contract_call;
use crate::utils;

pub fn get(args: BalanceArgs) -> Result<()> {
Expand Down Expand Up @@ -50,21 +51,19 @@ async fn get_balance<P>(
where
P: Provider,
{
fn handle_error(err: ContractCallError) -> Report {
let handle_error = |err: ProviderError| -> Report {
match err {
ContractCallError::Provider(ProviderError::StarknetError(
StarknetError::ContractNotFound,
)) => {
eyre!("token contract not found")
ProviderError::StarknetError(StarknetError::ContractNotFound) => {
eyre!("token with address '{address:#x}' is not found")
}
e => eyre!(e),
}
}
};

let retdata = contract_call(
provider,
contract_address,
"balanceOf",
selector!("balanceOf"),
vec![address],
block_id,
)
Expand Down Expand Up @@ -113,8 +112,14 @@ async fn get_decimals(
block_id: BlockId,
contract_address: FieldElement,
) -> Result<u8> {
let retdata =
contract_call(provider, contract_address, "decimals", Vec::new(), block_id).await?;
let retdata = contract_call(
provider,
contract_address,
selector!("decimals"),
Vec::new(),
block_id,
)
.await?;
let dec = retdata.first().context("missing value in call retdata")?;
Ok((*dec).try_into()?)
}
Expand All @@ -124,7 +129,14 @@ async fn get_symbol(
block_id: BlockId,
contract_address: FieldElement,
) -> Result<String> {
let retdata = contract_call(provider, contract_address, "symbol", Vec::new(), block_id).await?;
let retdata = contract_call(
provider,
contract_address,
selector!("symbol"),
Vec::new(),
block_id,
)
.await?;
let symbol = retdata.first().context("missing value in call retdata")?;
Ok(parse_cairo_short_string(symbol)?)
}
10 changes: 5 additions & 5 deletions crates/ops/src/rpc/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use starknet::{
providers::{Provider, ProviderError},
};

use crate::utils;
use super::utils;

pub fn age(args: AgeArgs) -> Result<()> {
let AgeArgs {
Expand All @@ -18,7 +18,7 @@ pub fn age(args: AgeArgs) -> Result<()> {
} = args;

let provider = starknet.provider();
let block = utils::block_on(get_block(provider, block_id))?;
let block = utils::do_call_with_mapped_rpc_err(get_block(provider, block_id))?;

let timestamp = match block {
MaybePendingBlockWithTxs::Block(b) => b.timestamp,
Expand Down Expand Up @@ -48,11 +48,11 @@ pub fn get(args: BlockArgs) -> Result<()> {
let provider = starknet.provider();

if compact {
let block = utils::block_on(get_block_compact(provider, id))?;
let block = utils::do_call_with_mapped_rpc_err(get_block_compact(provider, id))?;
display.display(block)?;
return Ok(());
} else {
let block = utils::block_on(get_block(provider, id))?;
let block = utils::do_call_with_mapped_rpc_err(get_block(provider, id))?;
if full || display.field.is_some() {
display.display(block)?;
} else {
Expand All @@ -65,7 +65,7 @@ pub fn get(args: BlockArgs) -> Result<()> {

pub fn number(args: BlockNumberArgs) -> Result<()> {
let provider = args.starknet.provider();
let number = utils::block_on(provider.block_number())?;
let number = utils::do_call_with_mapped_rpc_err(provider.block_number())?;
println!("{number:#x}");
Ok(())
}
Expand Down
45 changes: 21 additions & 24 deletions crates/ops/src/rpc/call.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use eyre::Result;
use eyre::{Context, Result};
use rika_args::{commands::rpc::CallArgs, fmt::Pretty};
use starknet::{
core::{
types::{BlockId, FieldElement, FunctionCall},
utils::{get_selector_from_name, NonAsciiNameError},
utils::get_selector_from_name,
},
providers::{Provider, ProviderError},
};

use crate::utils;
use super::utils;

// TODO: parse the return data according to the ABI?
pub fn call(args: CallArgs) -> Result<()> {
Expand All @@ -21,40 +21,37 @@ pub fn call(args: CallArgs) -> Result<()> {
} = args;

let provider = starknet.provider();
let retdata = utils::block_on(contract_call(

let selector = get_selector_from_name(&function)
.with_context(|| format!("invalid contract entrypoint name '{function}'"))?;
let retdata = utils::do_call_with_mapped_rpc_err(contract_call(
provider,
contract_address,
&function,
selector,
input,
block_id,
))?;

println!("{}", retdata.prettify());

Ok(())
}

#[derive(Debug, thiserror::Error)]
pub(crate) enum ContractCallError {
#[error("invalid contract entrypoint name: {0}")]
Selector(#[from] NonAsciiNameError),
#[error(transparent)]
Provider(#[from] ProviderError),
}

pub(crate) async fn contract_call<P: Provider>(
provider: P,
contract_address: FieldElement,
entrypoint: &str,
entry_point_selector: FieldElement,
calldata: Vec<FieldElement>,
block: BlockId,
) -> Result<Vec<FieldElement>, ContractCallError> {
let entrypoint = get_selector_from_name(entrypoint)?;
let request = FunctionCall {
calldata,
contract_address,
entry_point_selector: entrypoint,
};

let retdata = provider.call(request, block).await?;
Ok(retdata)
) -> Result<Vec<FieldElement>, ProviderError> {
Ok(provider
.call(
FunctionCall {
calldata,
contract_address,
entry_point_selector,
},
block,
)
.await?)
}
102 changes: 102 additions & 0 deletions crates/ops/src/rpc/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use starknet::core::types::{
ContractErrorData, NoTraceAvailableErrorData, StarknetError, TransactionExecutionErrorData,
};

#[derive(Debug, thiserror::Error)]
pub enum StarknetRpcError {
#[error("Failed to write transaction")]
FailedToReceiveTransaction,
#[error("Contract not found")]
ContractNotFound,
#[error("Block not found")]
BlockNotFound,
#[error("Invalid transaction index in a block")]
InvalidTransactionIndex,
#[error("Class hash not found")]
ClassHashNotFound,
#[error("Transaction hash not found")]
TransactionHashNotFound,
#[error("Requested page size is too big")]
PageSizeTooBig,
#[error("There are no blocks")]
NoBlocks,
#[error("The supplied continuation token is invalid or unknown")]
InvalidContinuationToken,
#[error("Too many keys provided in a filter")]
TooManyKeysInFilter,
#[error("Contract error: {}", _0.revert_error)]
ContractError(ContractErrorData),
#[error("Transaction execution error for transaction at index {}: {}", _0.transaction_index, _0.execution_error)]
TransactionExecutionError(TransactionExecutionErrorData),
#[error("Class already declared")]
ClassAlreadyDeclared,
#[error("Invalid transaction nonce")]
InvalidTransactionNonce,
#[error("Max fee is smaller than the minimal transaction cost (validation plus fee transfer)")]
InsufficientMaxFee,
#[error("Account balance is smaller than the transaction's max_fee")]
InsufficientAccountBalance,
#[error("Account validation failed")]
ValidationFailure(String),
#[error("Compilation failed")]
CompilationFailed,
#[error("Contract class size it too large")]
ContractClassSizeIsTooLarge,
#[error("Sender address in not an account contract")]
NonAccount,
#[error("A transaction with the same hash already exists in the mempool")]
DuplicateTx,
#[error("The compiled class hash did not match the one supplied in the transaction")]
CompiledClassHashMismatch,
#[error("The transaction version is not supported")]
UnsupportedTxVersion,
#[error("The contract class version is not supported")]
UnsupportedContractClassVersion,
#[error("An unexpected error occurred")]
UnexpectedError(String),
#[error("No trace available for transaction")]
NoTraceAvailable(NoTraceAvailableErrorData),
}

impl From<StarknetError> for StarknetRpcError {
fn from(error: StarknetError) -> Self {
match error {
StarknetError::FailedToReceiveTransaction => {
StarknetRpcError::FailedToReceiveTransaction
}
StarknetError::ContractNotFound => StarknetRpcError::ContractNotFound,
StarknetError::BlockNotFound => StarknetRpcError::BlockNotFound,
StarknetError::InvalidTransactionIndex => StarknetRpcError::InvalidTransactionIndex,
StarknetError::ClassHashNotFound => StarknetRpcError::ClassHashNotFound,
StarknetError::TransactionHashNotFound => StarknetRpcError::TransactionHashNotFound,
StarknetError::PageSizeTooBig => StarknetRpcError::PageSizeTooBig,
StarknetError::NoBlocks => StarknetRpcError::NoBlocks,
StarknetError::InvalidContinuationToken => StarknetRpcError::InvalidContinuationToken,
StarknetError::TooManyKeysInFilter => StarknetRpcError::TooManyKeysInFilter,
StarknetError::ContractError(data) => StarknetRpcError::ContractError(data),
StarknetError::TransactionExecutionError(data) => {
StarknetRpcError::TransactionExecutionError(data)
}
StarknetError::ClassAlreadyDeclared => StarknetRpcError::ClassAlreadyDeclared,
StarknetError::InvalidTransactionNonce => StarknetRpcError::InvalidTransactionNonce,
StarknetError::InsufficientMaxFee => StarknetRpcError::InsufficientMaxFee,
StarknetError::InsufficientAccountBalance => {
StarknetRpcError::InsufficientAccountBalance
}
StarknetError::ValidationFailure(msg) => StarknetRpcError::ValidationFailure(msg),
StarknetError::CompilationFailed => StarknetRpcError::CompilationFailed,
StarknetError::ContractClassSizeIsTooLarge => {
StarknetRpcError::ContractClassSizeIsTooLarge
}
StarknetError::NonAccount => StarknetRpcError::NonAccount,
StarknetError::DuplicateTx => StarknetRpcError::DuplicateTx,
StarknetError::CompiledClassHashMismatch => StarknetRpcError::CompiledClassHashMismatch,
StarknetError::UnsupportedTxVersion => StarknetRpcError::UnsupportedTxVersion,
StarknetError::UnsupportedContractClassVersion => {
StarknetRpcError::UnsupportedContractClassVersion
}
StarknetError::UnexpectedError(msg) => StarknetRpcError::UnexpectedError(msg),
StarknetError::NoTraceAvailable(data) => StarknetRpcError::NoTraceAvailable(data),
}
}
}
2 changes: 2 additions & 0 deletions crates/ops/src/rpc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod balance;
pub mod block;
pub mod call;
pub mod error;
pub mod raw;
pub mod transaction;
pub mod utils;
10 changes: 5 additions & 5 deletions crates/ops/src/rpc/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rika_args::{
};
use starknet::providers::Provider;

use crate::utils;
use super::utils;

pub fn get(args: TxArgs) -> Result<()> {
let TxArgs {
Expand All @@ -15,7 +15,7 @@ pub fn get(args: TxArgs) -> Result<()> {
} = args;

let provider = starknet.provider();
let result = utils::block_on(provider.get_transaction_by_hash(hash))?;
let result = utils::do_call_with_mapped_rpc_err(provider.get_transaction_by_hash(hash))?;
display.display(result)?;

Ok(())
Expand All @@ -25,7 +25,7 @@ pub fn count(args: TxCountArgs) -> Result<()> {
let TxCountArgs { block_id, starknet } = args;

let provider = starknet.provider();
let count = utils::block_on(provider.get_block_transaction_count(block_id))?;
let count = utils::do_call_with_mapped_rpc_err(provider.get_block_transaction_count(block_id))?;
println!("{count}");

Ok(())
Expand All @@ -35,7 +35,7 @@ pub fn status(args: TxStatusArgs) -> Result<()> {
let TxStatusArgs { hash, starknet } = args;

let provider = starknet.provider();
let status = utils::block_on(provider.get_transaction_status(hash))?;
let status = utils::do_call_with_mapped_rpc_err(provider.get_transaction_status(hash))?;
println!("{}", status.prettify());

Ok(())
Expand All @@ -49,7 +49,7 @@ pub fn receipt(args: ReceiptArgs) -> Result<()> {
} = args;

let provider = starknet.provider();
let receipt = utils::block_on(provider.get_transaction_receipt(hash))?;
let receipt = utils::do_call_with_mapped_rpc_err(provider.get_transaction_receipt(hash))?;
display.display(receipt)?;

Ok(())
Expand Down
17 changes: 17 additions & 0 deletions crates/ops/src/rpc/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::future::Future;

use eyre::{eyre, Result};
use starknet::providers::ProviderError;

use crate::{rpc::error::StarknetRpcError, utils};

pub(super) fn do_call_with_mapped_rpc_err<F, T>(fut: F) -> Result<T>
where
F: Future<Output = Result<T, ProviderError>>,
{
match utils::block_on(fut) {
Ok(res) => Ok(res),
Err(ProviderError::StarknetError(e)) => Err(eyre!(StarknetRpcError::from(e))),
Err(e) => Err(eyre!(e)),
}
}

0 comments on commit 0634b18

Please sign in to comment.