Skip to content

Commit 7b23df2

Browse files
authored
feat: parse data field for provider errors (#513)
1 parent 4107312 commit 7b23df2

File tree

5 files changed

+219
-76
lines changed

5 files changed

+219
-76
lines changed

starknet-accounts/tests/single_owner_account.rs

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
use rand::RngCore;
2-
use starknet_accounts::{Account, Call, ConnectedAccount, ExecutionEncoding, SingleOwnerAccount};
2+
use starknet_accounts::{
3+
Account, AccountError, Call, ConnectedAccount, ExecutionEncoding, SingleOwnerAccount,
4+
};
35
use starknet_core::{
46
chain_id,
57
types::{
68
contract::{
79
legacy::{LegacyContractClass, RawLegacyAbiEntry, RawLegacyFunction},
810
SierraClass,
911
},
10-
BlockId, BlockTag, FieldElement,
12+
BlockId, BlockTag, FieldElement, StarknetError,
1113
},
1214
utils::get_selector_from_name,
1315
};
1416
use starknet_providers::{
1517
jsonrpc::{HttpTransport, JsonRpcClient},
16-
Provider, SequencerGatewayProvider,
18+
Provider, ProviderError, SequencerGatewayProvider,
1719
};
1820
use starknet_signers::{LocalWallet, SigningKey};
1921
use std::sync::Arc;
@@ -66,6 +68,15 @@ async fn can_estimate_fee_with_jsonrpc() {
6668
.await
6769
}
6870

71+
#[tokio::test]
72+
async fn can_parse_fee_estimation_error_with_jsonrpc() {
73+
can_parse_fee_estimation_error_inner(
74+
create_jsonrpc_client(),
75+
"0x44c3c30803ea9c4e063ae052e6b7ef537284fca6b93849dae9a093e42aa1574",
76+
)
77+
.await
78+
}
79+
6980
// The `simulate`-related test cases are temporarily removed until it's supported in [Provider]
7081
// TODO: add `simulate` test cases back once transaction simulation in supported
7182

@@ -197,6 +208,54 @@ async fn can_estimate_fee_inner<P: Provider + Send + Sync>(provider: P, address:
197208
assert!(fee_estimate.overall_fee > 0);
198209
}
199210

211+
async fn can_parse_fee_estimation_error_inner<P: Provider + Send + Sync>(
212+
provider: P,
213+
address: &str,
214+
) {
215+
let signer = LocalWallet::from(SigningKey::from_secret_scalar(
216+
FieldElement::from_hex_be(
217+
"00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
218+
)
219+
.unwrap(),
220+
));
221+
let address = FieldElement::from_hex_be(address).unwrap();
222+
let eth_token_address = FieldElement::from_hex_be(
223+
"049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
224+
)
225+
.unwrap();
226+
227+
let mut account = SingleOwnerAccount::new(
228+
provider,
229+
signer,
230+
address,
231+
chain_id::TESTNET,
232+
ExecutionEncoding::Legacy,
233+
);
234+
account.set_block_id(BlockId::Tag(BlockTag::Pending));
235+
236+
match account
237+
.execute(vec![Call {
238+
to: eth_token_address,
239+
selector: get_selector_from_name("transfer").unwrap(),
240+
calldata: vec![
241+
address,
242+
FieldElement::from_dec_str("1000000000000000000000").unwrap(),
243+
FieldElement::ZERO,
244+
],
245+
}])
246+
.estimate_fee()
247+
.await
248+
{
249+
Ok(_) => panic!("unexpected successful fee estimation"),
250+
Err(AccountError::Provider(ProviderError::StarknetError(
251+
StarknetError::ContractError(err_data),
252+
))) => {
253+
assert!(!err_data.revert_error.is_empty());
254+
}
255+
_ => panic!("unexpected error type"),
256+
}
257+
}
258+
200259
async fn can_execute_tst_mint_inner<P: Provider + Send + Sync>(provider: P, address: &str) {
201260
// This test case is not very useful as the sequencer will always respond with
202261
// `TransactionReceived` even if the transaction will eventually fail, just like how

starknet-core/src/types/codegen.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// https://github.com/xJonathanLEI/starknet-jsonrpc-codegen
44

55
// Code generated with version:
6-
// https://github.com/xJonathanLEI/starknet-jsonrpc-codegen#51260963a0723fdbc715598efb7198ce5a1d49b9
6+
// https://github.com/xJonathanLEI/starknet-jsonrpc-codegen#bddc1b829c33b14440d22a85bc937e3d16e32ed1
77

88
// Code generation requested but not implemented for these types:
99
// - `BLOCK_ID`
@@ -241,6 +241,14 @@ pub struct CompressedLegacyContractClass {
241241
pub abi: Option<Vec<LegacyContractAbiEntry>>,
242242
}
243243

244+
/// More data about the execution failure.
245+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
246+
#[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))]
247+
pub struct ContractErrorData {
248+
/// A string encoding the execution trace up to the point of failure
249+
pub revert_error: String,
250+
}
251+
244252
/// Contract storage diff item.
245253
#[serde_as]
246254
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -1007,6 +1015,14 @@ pub struct MsgToL1 {
10071015
pub payload: Vec<FieldElement>,
10081016
}
10091017

1018+
/// Extra information on why trace is not available. Either it wasn't executed yet (received), or
1019+
/// the transaction failed (rejected).
1020+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1021+
#[cfg_attr(feature = "no_unknown_fields", serde(deny_unknown_fields))]
1022+
pub struct NoTraceAvailableErrorData {
1023+
pub status: SequencerTransactionStatus,
1024+
}
1025+
10101026
/// Nonce update.
10111027
///
10121028
/// The updated nonce per contract address.
@@ -1305,7 +1321,7 @@ pub enum SimulationFlag {
13051321
}
13061322

13071323
/// JSON-RPC error codes
1308-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1324+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13091325
pub enum StarknetError {
13101326
/// Failed to write transaction
13111327
FailedToReceiveTransaction,
@@ -1328,7 +1344,7 @@ pub enum StarknetError {
13281344
/// Too many keys provided in a filter
13291345
TooManyKeysInFilter,
13301346
/// Contract error
1331-
ContractError,
1347+
ContractError(ContractErrorData),
13321348
/// Class already declared
13331349
ClassAlreadyDeclared,
13341350
/// Invalid transaction nonce
@@ -1354,9 +1370,9 @@ pub enum StarknetError {
13541370
/// the contract class version is not supported
13551371
UnsupportedContractClassVersion,
13561372
/// An unexpected error occurred
1357-
UnexpectedError,
1373+
UnexpectedError(String),
13581374
/// No trace available for transaction
1359-
NoTraceAvailable,
1375+
NoTraceAvailable(NoTraceAvailableErrorData),
13601376
/// Invalid transaction hash
13611377
InvalidTransactionHash,
13621378
}
@@ -1377,7 +1393,7 @@ impl core::fmt::Display for StarknetError {
13771393
Self::NoBlocks => write!(f, "NoBlocks"),
13781394
Self::InvalidContinuationToken => write!(f, "InvalidContinuationToken"),
13791395
Self::TooManyKeysInFilter => write!(f, "TooManyKeysInFilter"),
1380-
Self::ContractError => write!(f, "ContractError"),
1396+
Self::ContractError(_) => write!(f, "ContractError"),
13811397
Self::ClassAlreadyDeclared => write!(f, "ClassAlreadyDeclared"),
13821398
Self::InvalidTransactionNonce => write!(f, "InvalidTransactionNonce"),
13831399
Self::InsufficientMaxFee => write!(f, "InsufficientMaxFee"),
@@ -1390,8 +1406,8 @@ impl core::fmt::Display for StarknetError {
13901406
Self::CompiledClassHashMismatch => write!(f, "CompiledClassHashMismatch"),
13911407
Self::UnsupportedTxVersion => write!(f, "UnsupportedTxVersion"),
13921408
Self::UnsupportedContractClassVersion => write!(f, "UnsupportedContractClassVersion"),
1393-
Self::UnexpectedError => write!(f, "UnexpectedError"),
1394-
Self::NoTraceAvailable => write!(f, "NoTraceAvailable"),
1409+
Self::UnexpectedError(_) => write!(f, "UnexpectedError"),
1410+
Self::NoTraceAvailable(_) => write!(f, "NoTraceAvailable"),
13951411
Self::InvalidTransactionHash => write!(f, "InvalidTransactionHash"),
13961412
}
13971413
}
@@ -1410,7 +1426,7 @@ impl StarknetError {
14101426
Self::NoBlocks => "There are no blocks",
14111427
Self::InvalidContinuationToken => "The supplied continuation token is invalid or unknown",
14121428
Self::TooManyKeysInFilter => "Too many keys provided in a filter",
1413-
Self::ContractError => "Contract error",
1429+
Self::ContractError(_) => "Contract error",
14141430
Self::ClassAlreadyDeclared => "Class already declared",
14151431
Self::InvalidTransactionNonce => "Invalid transaction nonce",
14161432
Self::InsufficientMaxFee => "Max fee is smaller than the minimal transaction cost (validation plus fee transfer)",
@@ -1423,8 +1439,8 @@ impl StarknetError {
14231439
Self::CompiledClassHashMismatch => "the compiled class hash did not match the one supplied in the transaction",
14241440
Self::UnsupportedTxVersion => "the transaction version is not supported",
14251441
Self::UnsupportedContractClassVersion => "the contract class version is not supported",
1426-
Self::UnexpectedError => "An unexpected error occurred",
1427-
Self::NoTraceAvailable => "No trace available for transaction",
1442+
Self::UnexpectedError(_) => "An unexpected error occurred",
1443+
Self::NoTraceAvailable(_) => "No trace available for transaction",
14281444
Self::InvalidTransactionHash => "Invalid transaction hash",
14291445
}
14301446
}

starknet-core/src/types/mod.rs

Lines changed: 13 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,19 @@ mod codegen;
1616
pub use codegen::{
1717
BlockStatus, BlockTag, BlockWithTxHashes, BlockWithTxs, BroadcastedDeclareTransactionV1,
1818
BroadcastedDeclareTransactionV2, BroadcastedDeployAccountTransaction,
19-
BroadcastedInvokeTransaction, CallType, CompressedLegacyContractClass, ContractStorageDiffItem,
20-
DataAvailabilityMode, DeclareTransactionReceipt, DeclareTransactionTrace, DeclareTransactionV0,
21-
DeclareTransactionV1, DeclareTransactionV2, DeclaredClassItem, DeployAccountTransaction,
22-
DeployAccountTransactionReceipt, DeployAccountTransactionTrace, DeployTransaction,
23-
DeployTransactionReceipt, DeployedContractItem, EmittedEvent, EntryPointType,
24-
EntryPointsByType, Event, EventFilter, EventFilterWithPage, EventsChunk, ExecutionResources,
25-
FeeEstimate, FlattenedSierraClass, FunctionCall, FunctionInvocation, FunctionStateMutability,
26-
InvokeTransactionReceipt, InvokeTransactionTrace, InvokeTransactionV0, InvokeTransactionV1,
27-
L1HandlerTransaction, L1HandlerTransactionReceipt, L1HandlerTransactionTrace,
28-
LegacyContractEntryPoint, LegacyEntryPointsByType, LegacyEventAbiEntry, LegacyEventAbiType,
29-
LegacyFunctionAbiEntry, LegacyFunctionAbiType, LegacyStructAbiEntry, LegacyStructAbiType,
30-
LegacyStructMember, LegacyTypedParameter, MsgFromL1, MsgToL1, NonceUpdate, OrderedEvent,
19+
BroadcastedInvokeTransaction, CallType, CompressedLegacyContractClass, ContractErrorData,
20+
ContractStorageDiffItem, DataAvailabilityMode, DeclareTransactionReceipt,
21+
DeclareTransactionTrace, DeclareTransactionV0, DeclareTransactionV1, DeclareTransactionV2,
22+
DeclaredClassItem, DeployAccountTransaction, DeployAccountTransactionReceipt,
23+
DeployAccountTransactionTrace, DeployTransaction, DeployTransactionReceipt,
24+
DeployedContractItem, EmittedEvent, EntryPointType, EntryPointsByType, Event, EventFilter,
25+
EventFilterWithPage, EventsChunk, ExecutionResources, FeeEstimate, FlattenedSierraClass,
26+
FunctionCall, FunctionInvocation, FunctionStateMutability, InvokeTransactionReceipt,
27+
InvokeTransactionTrace, InvokeTransactionV0, InvokeTransactionV1, L1HandlerTransaction,
28+
L1HandlerTransactionReceipt, L1HandlerTransactionTrace, LegacyContractEntryPoint,
29+
LegacyEntryPointsByType, LegacyEventAbiEntry, LegacyEventAbiType, LegacyFunctionAbiEntry,
30+
LegacyFunctionAbiType, LegacyStructAbiEntry, LegacyStructAbiType, LegacyStructMember,
31+
LegacyTypedParameter, MsgFromL1, MsgToL1, NoTraceAvailableErrorData, NonceUpdate, OrderedEvent,
3132
OrderedMessage, PendingBlockWithTxHashes, PendingBlockWithTxs,
3233
PendingDeclareTransactionReceipt, PendingDeployAccountTransactionReceipt,
3334
PendingInvokeTransactionReceipt, PendingL1HandlerTransactionReceipt, PendingStateUpdate,
@@ -516,42 +517,6 @@ impl TryFrom<&L1HandlerTransaction> for MsgToL2 {
516517
}
517518
}
518519

519-
impl TryFrom<i64> for StarknetError {
520-
type Error = ();
521-
522-
fn try_from(value: i64) -> Result<Self, Self::Error> {
523-
Ok(match value {
524-
1 => StarknetError::FailedToReceiveTransaction,
525-
20 => StarknetError::ContractNotFound,
526-
24 => StarknetError::BlockNotFound,
527-
27 => StarknetError::InvalidTransactionIndex,
528-
28 => StarknetError::ClassHashNotFound,
529-
29 => StarknetError::TransactionHashNotFound,
530-
31 => StarknetError::PageSizeTooBig,
531-
32 => StarknetError::NoBlocks,
532-
33 => StarknetError::InvalidContinuationToken,
533-
34 => StarknetError::TooManyKeysInFilter,
534-
40 => StarknetError::ContractError,
535-
51 => StarknetError::ClassAlreadyDeclared,
536-
52 => StarknetError::InvalidTransactionNonce,
537-
53 => StarknetError::InsufficientMaxFee,
538-
54 => StarknetError::InsufficientAccountBalance,
539-
55 => StarknetError::ValidationFailure,
540-
56 => StarknetError::CompilationFailed,
541-
57 => StarknetError::ContractClassSizeIsTooLarge,
542-
58 => StarknetError::NonAccount,
543-
59 => StarknetError::DuplicateTx,
544-
60 => StarknetError::CompiledClassHashMismatch,
545-
61 => StarknetError::UnsupportedTxVersion,
546-
62 => StarknetError::UnsupportedContractClassVersion,
547-
63 => StarknetError::UnexpectedError,
548-
10 => StarknetError::NoTraceAvailable,
549-
25 => StarknetError::InvalidTransactionHash,
550-
_ => return Err(()),
551-
})
552-
}
553-
}
554-
555520
#[cfg(test)]
556521
mod tests {
557522
use super::*;

0 commit comments

Comments
 (0)