Skip to content

Commit

Permalink
feat: erase implementation-specific provider error (#484)
Browse files Browse the repository at this point in the history
Changes the `Other` variant of `ProviderError` to use a trait object
instead of a generic parameter. This makes the `ProviderError` type much
easier to work with, as errors from different `Provider` types are now
compatible, and any type that wraps `ProviderError` will have one fewer
generic parameter to deal with.

At the same time, it's still possible, though harder, to access the
erased error type by downcasting, which requires the caller to know the
concrete type at compile time, as showcased in the new example.

This is a breaking change, but it shouldn't affect downstream
applications which already erase the whole `ProviderError` type with
something like `anyhow`.
  • Loading branch information
xJonathanLEI authored Oct 29, 2023
1 parent 33e200d commit c166e93
Show file tree
Hide file tree
Showing 13 changed files with 443 additions and 558 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ Examples can be found in the [examples folder](./examples):

9. [Parsing a JSON-RPC request on the server side](./examples/parse_jsonrpc_request.rs)

10. [Inspecting a erased provider-specific error type](./examples/downcast_provider_error.rs)

## License

Licensed under either of
Expand Down
26 changes: 26 additions & 0 deletions examples/downcast_provider_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use starknet_providers::{
jsonrpc::{HttpTransport, HttpTransportError, JsonRpcClient, JsonRpcClientError},
Provider, ProviderError,
};
use url::Url;

#[tokio::main]
async fn main() {
let rpc_client =
JsonRpcClient::new(HttpTransport::new(Url::parse("https://fake.url/").unwrap()));

let error = match rpc_client.block_number().await.unwrap_err() {
ProviderError::Other(inner) => inner,
_ => panic!("unexpected error variant"),
};

// The implementation-specific error type is erased to make the `ProviderError` type easier to
// work with. Here, we showcase how to recover the inner type.
let impl_specific_error: &JsonRpcClientError<HttpTransportError> =
match error.as_any().downcast_ref() {
Some(inner) => inner,
None => panic!("unexpected downcast failure"),
};

dbg!(impl_specific_error);
}
74 changes: 16 additions & 58 deletions starknet-accounts/src/account/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ impl<'a, A> Declaration<'a, A>
where
A: ConnectedAccount + Sync,
{
pub async fn estimate_fee(
&self,
) -> Result<FeeEstimate, AccountError<A::SignError, <A::Provider as Provider>::Error>> {
pub async fn estimate_fee(&self) -> Result<FeeEstimate, AccountError<A::SignError>> {
// Resolves nonce
let nonce = match self.nonce {
Some(value) => value,
Expand All @@ -119,8 +117,7 @@ where
&self,
skip_validate: bool,
skip_fee_charge: bool,
) -> Result<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::Error>>
{
) -> Result<SimulatedTransaction, AccountError<A::SignError>> {
// Resolves nonce
let nonce = match self.nonce {
Some(value) => value,
Expand All @@ -135,21 +132,11 @@ where
.await
}

pub async fn send(
&self,
) -> Result<
DeclareTransactionResult,
AccountError<A::SignError, <A::Provider as Provider>::Error>,
> {
pub async fn send(&self) -> Result<DeclareTransactionResult, AccountError<A::SignError>> {
self.prepare().await?.send().await
}

async fn prepare(
&self,
) -> Result<
PreparedDeclaration<'a, A>,
AccountError<A::SignError, <A::Provider as Provider>::Error>,
> {
async fn prepare(&self) -> Result<PreparedDeclaration<'a, A>, AccountError<A::SignError>> {
// Resolves nonce
let nonce = match self.nonce {
Some(value) => value,
Expand Down Expand Up @@ -183,7 +170,7 @@ where
async fn estimate_fee_with_nonce(
&self,
nonce: FieldElement,
) -> Result<FeeEstimate, AccountError<A::SignError, <A::Provider as Provider>::Error>> {
) -> Result<FeeEstimate, AccountError<A::SignError>> {
let prepared = PreparedDeclaration {
account: self.account,
inner: RawDeclaration {
Expand All @@ -210,8 +197,7 @@ where
nonce: FieldElement,
skip_validate: bool,
skip_fee_charge: bool,
) -> Result<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::Error>>
{
) -> Result<SimulatedTransaction, AccountError<A::SignError>> {
let prepared = PreparedDeclaration {
account: self.account,
inner: RawDeclaration {
Expand Down Expand Up @@ -298,9 +284,7 @@ impl<'a, A> LegacyDeclaration<'a, A>
where
A: ConnectedAccount + Sync,
{
pub async fn estimate_fee(
&self,
) -> Result<FeeEstimate, AccountError<A::SignError, <A::Provider as Provider>::Error>> {
pub async fn estimate_fee(&self) -> Result<FeeEstimate, AccountError<A::SignError>> {
// Resolves nonce
let nonce = match self.nonce {
Some(value) => value,
Expand All @@ -318,8 +302,7 @@ where
&self,
skip_validate: bool,
skip_fee_charge: bool,
) -> Result<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::Error>>
{
) -> Result<SimulatedTransaction, AccountError<A::SignError>> {
// Resolves nonce
let nonce = match self.nonce {
Some(value) => value,
Expand All @@ -334,21 +317,13 @@ where
.await
}

pub async fn send(
&self,
) -> Result<
DeclareTransactionResult,
AccountError<A::SignError, <A::Provider as Provider>::Error>,
> {
pub async fn send(&self) -> Result<DeclareTransactionResult, AccountError<A::SignError>> {
self.prepare().await?.send().await
}

async fn prepare(
&self,
) -> Result<
PreparedLegacyDeclaration<'a, A>,
AccountError<A::SignError, <A::Provider as Provider>::Error>,
> {
) -> Result<PreparedLegacyDeclaration<'a, A>, AccountError<A::SignError>> {
// Resolves nonce
let nonce = match self.nonce {
Some(value) => value,
Expand Down Expand Up @@ -381,7 +356,7 @@ where
async fn estimate_fee_with_nonce(
&self,
nonce: FieldElement,
) -> Result<FeeEstimate, AccountError<A::SignError, <A::Provider as Provider>::Error>> {
) -> Result<FeeEstimate, AccountError<A::SignError>> {
let prepared = PreparedLegacyDeclaration {
account: self.account,
inner: RawLegacyDeclaration {
Expand All @@ -407,8 +382,7 @@ where
nonce: FieldElement,
skip_validate: bool,
skip_fee_charge: bool,
) -> Result<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::Error>>
{
) -> Result<SimulatedTransaction, AccountError<A::SignError>> {
let prepared = PreparedLegacyDeclaration {
account: self.account,
inner: RawLegacyDeclaration {
Expand Down Expand Up @@ -505,12 +479,7 @@ impl<'a, A> PreparedDeclaration<'a, A>
where
A: ConnectedAccount,
{
pub async fn send(
&self,
) -> Result<
DeclareTransactionResult,
AccountError<A::SignError, <A::Provider as Provider>::Error>,
> {
pub async fn send(&self) -> Result<DeclareTransactionResult, AccountError<A::SignError>> {
let tx_request = self.get_declare_request(false).await?;
self.account
.provider()
Expand All @@ -522,10 +491,7 @@ where
pub async fn get_declare_request(
&self,
query_only: bool,
) -> Result<
BroadcastedDeclareTransactionV2,
AccountError<A::SignError, <A::Provider as Provider>::Error>,
> {
) -> Result<BroadcastedDeclareTransactionV2, AccountError<A::SignError>> {
let signature = self
.account
.sign_declaration(&self.inner, query_only)
Expand Down Expand Up @@ -563,12 +529,7 @@ impl<'a, A> PreparedLegacyDeclaration<'a, A>
where
A: ConnectedAccount,
{
pub async fn send(
&self,
) -> Result<
DeclareTransactionResult,
AccountError<A::SignError, <A::Provider as Provider>::Error>,
> {
pub async fn send(&self) -> Result<DeclareTransactionResult, AccountError<A::SignError>> {
let tx_request = self.get_declare_request(false).await?;
self.account
.provider()
Expand All @@ -580,10 +541,7 @@ where
pub async fn get_declare_request(
&self,
query_only: bool,
) -> Result<
BroadcastedDeclareTransactionV1,
AccountError<A::SignError, <A::Provider as Provider>::Error>,
> {
) -> Result<BroadcastedDeclareTransactionV1, AccountError<A::SignError>> {
let signature = self
.account
.sign_legacy_declaration(&self.inner, query_only)
Expand Down
29 changes: 7 additions & 22 deletions starknet-accounts/src/account/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ impl<'a, A> Execution<'a, A>
where
A: ConnectedAccount + Sync,
{
pub async fn estimate_fee(
&self,
) -> Result<FeeEstimate, AccountError<A::SignError, <A::Provider as Provider>::Error>> {
pub async fn estimate_fee(&self) -> Result<FeeEstimate, AccountError<A::SignError>> {
// Resolves nonce
let nonce = match self.nonce {
Some(value) => value,
Expand All @@ -102,8 +100,7 @@ where
&self,
skip_validate: bool,
skip_fee_charge: bool,
) -> Result<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::Error>>
{
) -> Result<SimulatedTransaction, AccountError<A::SignError>> {
// Resolves nonce
let nonce = match self.nonce {
Some(value) => value,
Expand All @@ -118,19 +115,11 @@ where
.await
}

pub async fn send(
&self,
) -> Result<InvokeTransactionResult, AccountError<A::SignError, <A::Provider as Provider>::Error>>
{
pub async fn send(&self) -> Result<InvokeTransactionResult, AccountError<A::SignError>> {
self.prepare().await?.send().await
}

async fn prepare(
&self,
) -> Result<
PreparedExecution<'a, A>,
AccountError<A::SignError, <A::Provider as Provider>::Error>,
> {
async fn prepare(&self) -> Result<PreparedExecution<'a, A>, AccountError<A::SignError>> {
// Resolves nonce
let nonce = match self.nonce {
Some(value) => value,
Expand Down Expand Up @@ -163,7 +152,7 @@ where
async fn estimate_fee_with_nonce(
&self,
nonce: FieldElement,
) -> Result<FeeEstimate, AccountError<A::SignError, <A::Provider as Provider>::Error>> {
) -> Result<FeeEstimate, AccountError<A::SignError>> {
let prepared = PreparedExecution {
account: self.account,
inner: RawExecution {
Expand Down Expand Up @@ -192,8 +181,7 @@ where
nonce: FieldElement,
skip_validate: bool,
skip_fee_charge: bool,
) -> Result<SimulatedTransaction, AccountError<A::SignError, <A::Provider as Provider>::Error>>
{
) -> Result<SimulatedTransaction, AccountError<A::SignError>> {
let prepared = PreparedExecution {
account: self.account,
inner: RawExecution {
Expand Down Expand Up @@ -276,10 +264,7 @@ impl<'a, A> PreparedExecution<'a, A>
where
A: ConnectedAccount,
{
pub async fn send(
&self,
) -> Result<InvokeTransactionResult, AccountError<A::SignError, <A::Provider as Provider>::Error>>
{
pub async fn send(&self) -> Result<InvokeTransactionResult, AccountError<A::SignError>> {
let tx_request = self
.get_invoke_request(false)
.await
Expand Down
8 changes: 3 additions & 5 deletions starknet-accounts/src/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,7 @@ pub trait ConnectedAccount: Account {
BlockId::Tag(BlockTag::Latest)
}

async fn get_nonce(
&self,
) -> Result<FieldElement, ProviderError<<Self::Provider as Provider>::Error>> {
async fn get_nonce(&self) -> Result<FieldElement, ProviderError> {
self.provider()
.get_nonce(self.block_id(), self.address())
.await
Expand Down Expand Up @@ -170,11 +168,11 @@ pub struct PreparedLegacyDeclaration<'a, A> {
}

#[derive(Debug, thiserror::Error)]
pub enum AccountError<S, P> {
pub enum AccountError<S> {
#[error(transparent)]
Signing(S),
#[error(transparent)]
Provider(ProviderError<P>),
Provider(ProviderError),
#[error(transparent)]
ClassHashCalculation(ComputeClassHashError),
#[error(transparent)]
Expand Down
Loading

0 comments on commit c166e93

Please sign in to comment.