Skip to content

Conversation

Abeeujah
Copy link
Contributor

@Abeeujah Abeeujah commented Sep 15, 2025

Usage related changes

N/A

Development related changes

Custom assertion methods now return results instead of panicking. This change improves debugging as the stack trace is more intuitive to reason about.

The following custom assertion methods were modified to return a result instead of panicking:

  • assert_tx_succeeded_accepted
  • assert_tx_succeeded_pre_confirmed
  • assert_tx_reverted
  • assert_equal_elements
  • assert_no_notifications
  • assert_contains
  • extract_message_error
  • extract_nested_error
  • get_contract_balance
  • get_contract_balance_by_block_id
  • assert_tx_aborted

closes #845

Checklist:

  • Checked out the contribution guidelines
  • Applied formatting - ./scripts/format.sh
  • No linter errors - ./scripts/clippy_check.sh
  • No unused dependencies - ./scripts/check_unused_deps.sh
  • No spelling errors - ./scripts/check_spelling.sh
  • Performed code self-review
  • Rebased to the latest commit of the target branch (or merged it into my branch)
    • Once you make the PR reviewable, please avoid force-pushing
  • Updated the docs if needed - ./website/README.md
  • Linked the issues resolvable by this PR - linking info
  • Updated the tests if needed; all passing - execution info

Summary by CodeRabbit

  • Refactor
    • Test helpers now propagate explicit errors instead of panicking, with typed receipt handling for clearer outcomes.
  • Tests
    • Integration tests updated to enforce and unwrap helper results, making assertions stricter and failures immediate.
    • Strengthened nested error/message checks and notification validations for websockets/subscriptions.
    • Overall improves test robustness, diagnostics, and determinism without changing product behavior.

Copy link

coderabbitai bot commented Sep 15, 2025

Walkthrough

Refactors many test helpers in tests/integration/common/utils.rs and related test files to return Result<(), anyhow::Error> (replacing panic-based handling). Helpers updated include assert_tx_succeeded_accepted, get_contract_balance(_by_block_id), assert_tx_reverted, assert_equal_elements, extract_message_error, extract_nested_error, assert_no_notifications, assert_contains, and several block/receipt/balance helpers. TransactionReceipt is used for typed receipt handling. Numerous integration tests across transactions, receipts, block generation, subscriptions (blocks/events/txs/reorg/tx status), fee estimation, advancing time, forking, messaging, deployments, v3 transactions, tracing, minting, dump/load, and websockets updated to handle the new Result returns (mostly by appending .await.unwrap() or unwrapping nested extraction helpers). RpcError gained an impl of std::error::Error.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Custom assertion methods return Result" is a concise, single-sentence summary that accurately captures the primary change in the diff — converting custom assertion helpers to return Result types instead of panicking. It directly reflects the scope of modifications shown in the raw_summary and is specific enough for a reviewer scanning history. No extraneous wording or ambiguity is present.
Linked Issues Check ✅ Passed The changes implement the goals of the linked issue [#845] by converting numerous test helper assertion functions to return Result and updating their call sites so failures surface with clearer stack traces. The raw_summary and pr_objectives show widespread, consistent edits across test helpers and consumers (assert_tx_, assert_contains, extract_, balance helpers, etc.), and there are no indicated deviations from the issue's scope. Therefore the PR aligns with and satisfies the coding-related requirements of the linked issue.
Out of Scope Changes Check ✅ Passed The modifications appear confined to test code and test helper APIs (files under tests/ and shared test utils) and support error propagation; the raw_summary shows no production/public API changes outside tests. Supplemental edits (for example implementing std::error::Error for RpcError) are within test support code and consistent with the refactor. I do not see unrelated or out-of-scope changes in the provided diff summary.
Description Check ✅ Passed The PR description follows the repository's template by including "Usage related changes" (marked N/A), a "Development related changes" section that lists the modified helpers, and a completed Checklist; it also cites the linked issue and intent to improve debugging. The description enumerates the key functions changed and the motivation, giving reviewers the necessary context and scope. Overall the description is complete and matches the template structure.
Docstring Coverage ✅ Passed Docstring coverage is 86.08% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@FabijanC
Copy link
Contributor

I changed the PR description because there are no usage related changes (from the perspective of those parties that just use and don't develop Devnet).

@coderabbitai review

Copy link

coderabbitai bot commented Sep 16, 2025

@FabijanC Thank you for the clarification on the PR description. You're absolutely right that these changes are purely development-focused and don't impact users who simply consume Devnet. The distinction between usage-related and development-related changes is important for clarity.

I'll review the changes where custom assertion methods now return Result instead of panicking, which should indeed provide better debugging experience with more intuitive stack traces.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/integration/common/utils.rs (1)

146-163: Avoid panic inside Result-returning helper

assert_eq!(res.len(), 1) at tests/integration/common/utils.rs:158 still panics; return an anyhow::Error with context instead.

-    match devnet.json_rpc_client.call(contract_call, block_id).await {
-        Ok(res) => {
-            assert_eq!(res.len(), 1);
-            Ok(res[0])
-        }
-        Err(e) => anyhow::bail!("Call failed: {e}"),
-    }
+    match devnet.json_rpc_client.call(contract_call, block_id).await {
+        Ok(res) => {
+            if res.len() != 1 {
+                anyhow::bail!(
+                    "Expected single return from get_balance at {contract_address:#x}; got len={} (resp={res:?})",
+                    res.len()
+                );
+            }
+            Ok(res[0])
+        }
+        Err(e) => anyhow::bail!("Call failed (block_id={block_id:?}): {e}"),
+    }
🧹 Nitpick comments (31)
tests/integration/test_abort_blocks.rs (1)

36-47: Include tx hash in error for easier debugging

If this ever fails, the tx hash in the error is handy.

-        match devnet.json_rpc_client.get_transaction_by_hash(tx_hash).await {
+        match devnet.json_rpc_client.get_transaction_by_hash(tx_hash).await {
             Err(ProviderError::StarknetError(StarknetError::TransactionHashNotFound)) => (),
-            other => anyhow::bail!("Unexpected tx response: {other:?}"),
+            other => anyhow::bail!("Unexpected tx response for {tx_hash:#x}: {other:?}"),
         }
tests/integration/test_fork.rs (1)

309-312: Minor: simplify the match into unwrap_or_else

Functionally identical, but a tad cleaner.

-match assert_tx_succeeded_accepted(&result.transaction_hash, &devnet.json_rpc_client).await {
-    Ok(_) => (),
-    Err(e) => panic!("Transaction failed: {}", e),
-};
+assert_tx_succeeded_accepted(&result.transaction_hash, &devnet.json_rpc_client)
+    .await
+    .unwrap_or_else(|e| panic!("Transaction failed: {e}"));
tests/integration/test_v3_transactions.rs (1)

309-312: Minor: prefer unwrap_or_else over manual match

Same behavior, slightly tighter.

-match assert_tx_succeeded_accepted(&result.transaction_hash, &devnet.json_rpc_client).await {
-    Ok(_) => (),
-    Err(e) => panic!("Transaction failed: {}", e),
-};
+assert_tx_succeeded_accepted(&result.transaction_hash, &devnet.json_rpc_client)
+    .await
+    .unwrap_or_else(|e| panic!("Transaction failed: {e}"));
tests/integration/common/utils.rs (4)

105-123: Add tx-hash context to errors for faster triage

Bubbling provider errors is fine, but including the tx hash in messages makes debugging much faster. Also include the tx hash in assertion failures.

 pub async fn assert_tx_succeeded_accepted<T: Provider>(
     tx_hash: &Felt,
     client: &T,
 ) -> Result<(), anyhow::Error> {
-    let receipt = match client.get_transaction_receipt(tx_hash).await {
-        Ok(receipt) => receipt.receipt,
-        Err(err) => return Err(err.into()),
-    };
+    let receipt = match client.get_transaction_receipt(tx_hash).await {
+        Ok(receipt) => receipt.receipt,
+        Err(err) => return Err(anyhow::anyhow!("Failed to fetch receipt for {tx_hash:#x}: {err}")),
+    };

     match receipt.execution_result() {
         ExecutionResult::Succeeded => (),
-        other => anyhow::bail!("Should have succeeded; got: {other:?}"),
+        other => anyhow::bail!("Tx {tx_hash:#x} should have succeeded; got: {other:?}"),
     }

     match receipt.finality_status() {
-        starknet_rs_core::types::TransactionFinalityStatus::AcceptedOnL2 => Ok(()),
-        other => anyhow::bail!("Should have been accepted on L2; got: {other:?}"),
+        starknet_rs_core::types::TransactionFinalityStatus::AcceptedOnL2 => Ok(()),
+        other => anyhow::bail!("Tx {tx_hash:#x} should have been accepted on L2; got: {other:?}"),
     }
 }

165-187: Include tx hash in revert-path errors

Helpful when reading CI logs.

-        other => anyhow::bail!("Should have reverted; got: {other:?}; receipt: {receipt:?}"),
+        other => anyhow::bail!(
+            "Tx {tx_hash:#x} should have reverted; got: {other:?}; receipt: {receipt:?}"
+        ),

457-473: Remove panic and redundant length check

You both assert and then check lengths; keep a single non-panicking path that returns an error.

-pub fn assert_equal_elements<T>(iterable1: &[T], iterable2: &[T]) -> Result<(), anyhow::Error>
+pub fn assert_equal_elements<T>(iterable1: &[T], iterable2: &[T]) -> Result<(), anyhow::Error>
 where
     T: PartialEq,
 {
-    assert_eq!(iterable1.len(), iterable2.len());
-    match iterable1.len() == iterable2.len() {
-        true => (),
-        false => return Err(anyhow::anyhow!("Length mismatch")),
-    }
+    if iterable1.len() != iterable2.len() {
+        return Err(anyhow::anyhow!(
+            "Length mismatch: left={}, right={}",
+            iterable1.len(),
+            iterable2.len()
+        ));
+    }
     for e in iterable1 {
-        match iterable2.contains(e) {
-            true => (),
-            false => return Err(anyhow::anyhow!("Element mismatch")),
-        }
+        if !iterable2.contains(e) {
+            return Err(anyhow::anyhow!("Element mismatch"));
+        }
     }
     Ok(())
 }

612-622: Don’t rely on error strings for timeout detection

String matching on “deadline has elapsed” is brittle. Downcast to tokio’s Elapsed instead.

-    match receive_rpc_via_ws(ws).await {
-        Ok(resp) => anyhow::bail!("Expected no notifications; found: {resp}"),
-        Err(e) if e.to_string().contains("deadline has elapsed") => {
-            /* expected */
-            Ok(())
-        }
-        Err(e) => anyhow::bail!("Expected to error out due to empty channel; found: {e}"),
-    }
+    match receive_rpc_via_ws(ws).await {
+        Ok(resp) => anyhow::bail!("Expected no notifications; found: {resp}"),
+        Err(e) if e.downcast_ref::<tokio::time::error::Elapsed>().is_some() => Ok(()), // expected
+        Err(e) => anyhow::bail!("Expected timeout due to empty channel; found: {e}"),
+    }
tests/integration/test_subscription_with_invalid_block_id.rs (1)

37-37: LGTM: unwrap on assert_no_notifications matches new helper contract.

Consider .expect("no notifications expected") for slightly clearer failures, but optional.

Also applies to: 61-61

tests/integration/test_subscription_to_new_txs.rs (1)

27-27: DRY option: wrap repeated .await.unwrap() in a tiny macro.

Many identical calls to assert_no_notifications(&mut ws).await.unwrap();. Optional macro reduces noise:

+// in a common test module (e.g., tests/integration/common/mod.rs) or at file top:
+macro_rules! assert_no_notifs {
+    ($ws:expr) => {{
+        crate::common::utils::assert_no_notifications($ws)
+            .await
+            .expect("Unexpected WS notifications");
+    }};
+}

Then replace lines like:

-assert_no_notifications(&mut ws).await.unwrap();
+assert_no_notifs!(&mut ws);

Also applies to: 47-47, 75-75, 98-98, 123-123, 161-161, 175-175, 195-195, 217-217, 237-237, 252-252, 269-269, 289-289, 311-311, 315-315, 334-334

tests/integration/test_subscription_to_events.rs (1)

107-107: LGTM; optional helper to reduce repetition.

Same macro pattern as suggested in new_txs can shorten these .await.unwrap() repetitions and standardize the failure message.

Also applies to: 112-112, 142-142, 173-173, 203-203, 249-250, 315-316, 339-340, 392-392, 425-425, 444-444, 495-495, 510-510, 553-553

tests/integration/test_subscription_to_tx_status.rs (1)

85-85: LGTM; consider a shared macro for clearer, consistent failures.

Adopt the assert_no_notifs! macro (see earlier comment) to reduce boilerplate and give uniform panic text.

Also applies to: 94-94, 109-109, 125-125, 136-136, 162-162, 175-175, 211-211, 229-229, 234-234, 256-256, 257-257, 275-275, 299-299, 321-321

tests/integration/test_subscription_to_reorg.rs (1)

62-62: LGTM; optional macro for the no-notifications checks.

Switching to a shared assert_no_notifs! macro would keep these concise across tests.

Also applies to: 125-125

tests/integration/test_account_impersonation.rs (3)

203-203: Good: helpers now return Result; unwrapping at call sites matches PR intent.

Using .unwrap() on assert_contains(...) centralizes failures at the call site and improves trace quality. Consider .expect("context") where extra context helps future debugging.

Also applies to: 271-272


269-272: Make contains-check robust to caller casing.

Lowercase both haystack and needle to avoid case fragility.

 fn assert_anyhow_error_contains_message(error: anyhow::Error, message: &str) {
   let error_string = format!("{:?}", error.root_cause()).to_lowercase();
-  assert_contains(&error_string, message).unwrap();
+  let message = message.to_lowercase();
+  assert_contains(&error_string, &message).unwrap();
 }

245-265: Balance assertion is directionally correct; consider clarifying intent.

initial >= amount + final implies ≥ amount spent (plus fees). A short comment can prevent misreads (amount vs fee vs token).

tests/integration/test_call.rs (1)

142-154: LGTM: Result-returning extractors now unwrapped at the assertion site.

This preserves precise failure locations. If failures are noisy, switch to .expect("failed to extract nested error") for clarity.

tests/integration/test_subscription_to_new_tx_receipts.rs (1)

52-52: LGTM: assert_no_notifications unwrapped across scenarios.

Pattern is consistent and improves failure locality. If this becomes repetitive, consider a tiny helper assert_no_notifications_ok(ws).await? in utils and let tests return anyhow::Result<()>.

Also applies to: 72-72, 104-104, 129-129, 154-154, 177-177, 216-216, 231-231, 250-250, 271-271, 293-293, 309-309, 328-328, 350-350, 377-377, 381-381, 403-403

tests/integration/test_advancing_time_on_fork.rs (2)

67-79: Explicit unwraps match the new Result-returning helpers — OK; consider expect(...) for context.

Current approach satisfies PR goals. Optionally add short expect("extraction failed at <step>") messages to speed up triage when this fails.


142-142: Guard against missing revert_reason to avoid a panic.

revert_reason() returns an Option; use expect to provide context if it’s None.

-            assert_contains(tx_details.revert_reason().unwrap(), "Wait a bit more").unwrap();
+            assert_contains(
+                tx_details.revert_reason().as_deref().expect("missing revert_reason"),
+                "Wait a bit more",
+            ).unwrap();
tests/integration/test_deploy.rs (1)

71-82: Result-based error extraction and assertions look good.

Unwraps are consistent with the helpers now returning Result. If you want slightly clearer failures, swap a few unwrap()s for expect("reason") (optional).

Also applies to: 111-111, 140-141, 166-167

tests/integration/test_advancing_time.rs (2)

631-643: Good: explicit unwraps on nested/message extraction align with the new signatures.

No functional issues spotted. Optional: replace some unwrap()s with expect("context") to make failures self-descriptive.


689-689: Minor: provide context when unwrapping revert reason.

Same nit as elsewhere; expect("missing revert_reason") would clarify failures.

tests/integration/test_estimate_fee.rs (2)

279-289: Simplify revert assertion control flow.

Use expect to shorten and keep intent crisp.

-    match assert_tx_reverted(
-        &unsuccessful_invoke_tx.transaction_hash,
-        &devnet.json_rpc_client,
-        &["Insufficient max L2Gas"],
-    )
-    .await
-    {
-        Ok(_) => {}
-        Err(e) => panic!("Transaction not reverted: {:?}", e),
-    };
+    assert_tx_reverted(
+        &unsuccessful_invoke_tx.transaction_hash,
+        &devnet.json_rpc_client,
+        &["Insufficient max L2Gas"],
+    )
+    .await
+    .expect("Transaction not reverted");

367-372: Optional: factor repetitive nested-error unwrap chain into a tiny helper.

A small test-only utility like fn deepest_msg(err: &ExecutionError) -> String would remove repetition across tests.

tests/integration/test_messaging.rs (1)

1041-1041: Minor: stabilize revert reason unwrapping.

Consider expect("missing revert_reason") for clearer panic if provider omits it.

tests/integration/test_accepting_blocks_on_l1.rs (2)

21-37: Make helper fully Result-based (avoid assert!/unwrap! inside).

Propagate errors via ?/ensure! to align with the PR goal and improve failure locality.

Apply:

 async fn assert_accepted_on_l1(
     devnet: &BackgroundDevnet,
     block_hashes: &[Felt],
     tx_hashes: &[Felt],
 ) -> Result<(), anyhow::Error> {
     for block_hash in block_hashes {
         match devnet.json_rpc_client.get_block_with_tx_hashes(BlockId::Hash(*block_hash)).await {
-            Ok(MaybePreConfirmedBlockWithTxHashes::Block(block)) => {
-                assert_eq!(block.status, BlockStatus::AcceptedOnL1)
-            }
+            Ok(MaybePreConfirmedBlockWithTxHashes::Block(block)) => {
+                anyhow::ensure!(
+                    block.status == BlockStatus::AcceptedOnL1,
+                    "expected block {block_hash:?} to be ACCEPTED_ON_L1, got {:?}",
+                    block.status
+                );
+            }
             other => anyhow::bail!("Unexpected block: {other:?}"),
         }
     }
 
     for tx_hash in tx_hashes {
-        let tx_status = devnet.json_rpc_client.get_transaction_status(tx_hash).await.unwrap();
-        assert_eq!(tx_status.finality_status(), SequencerTransactionStatus::AcceptedOnL1);
+        let tx_status = devnet.json_rpc_client.get_transaction_status(tx_hash).await?;
+        anyhow::ensure!(
+            tx_status.finality_status() == SequencerTransactionStatus::AcceptedOnL1,
+            "expected tx {tx_hash:?} to be ACCEPTED_ON_L1, got {:?}",
+            tx_status.finality_status()
+        );
     }
 
     Ok(())
 }

68-69: Consistent unwrap messaging at call sites.

Optional: use .expect("assert_accepted_on_l1 failed") for clearer failure context.

Example:

-assert_accepted_on_l1(&devnet, &block_hashes, &tx_hashes).await.unwrap();
+assert_accepted_on_l1(&devnet, &block_hashes, &tx_hashes)
+    .await
+    .expect("assert_accepted_on_l1 failed");

Also applies to: 90-91, 112-114, 135-136

tests/integration/test_account_selection.rs (2)

152-160: Simplify to expect/unwrap for consistency.

Replace verbose match + panic with .await.expect(...) to match surrounding style.

-    match assert_tx_succeeded_accepted(
-        &account_deployment.transaction_hash,
-        &devnet.json_rpc_client,
-    )
-    .await
-    {
-        Ok(_) => (),
-        Err(e) => panic!("Transaction failed: {}", e),
-    };
+    assert_tx_succeeded_accepted(
+        &account_deployment.transaction_hash,
+        &devnet.json_rpc_client,
+    )
+    .await
+    .expect("Transaction failed");

81-83: Optional: add context to unwraps.

Use .expect("tx should be ACCEPTED_ON_L2") to improve failure messages.

Also applies to: 95-97, 109-111, 138-140, 214-216

tests/integration/test_blocks_generation.rs (2)

100-127: Make assert_latest_block_with_receipts fully Result-driven (no panics).

Use ensure! and ? to avoid assert_eq! panics and manual matches.

 async fn assert_latest_block_with_receipts(
     devnet: &BackgroundDevnet,
     block_number: u64,
     tx_count: usize,
 ) -> Result<(), anyhow::Error> {
     let latest_block = &devnet
         .send_custom_rpc("starknet_getBlockWithReceipts", json!({ "block_id": "latest" }))
         .await
         .unwrap();
 
-    assert_eq!(latest_block["transactions"].as_array().unwrap().len(), tx_count);
-    assert_eq!(latest_block["block_number"], block_number);
-    assert_eq!(latest_block["status"], "ACCEPTED_ON_L2");
+    anyhow::ensure!(
+        latest_block["transactions"].as_array().unwrap().len() == tx_count,
+        "unexpected tx count in latest block"
+    );
+    anyhow::ensure!(latest_block["block_number"] == block_number, "unexpected block number");
+    anyhow::ensure!(latest_block["status"] == "ACCEPTED_ON_L2", "unexpected block status");
 
     for tx in latest_block["transactions"].as_array().unwrap() {
-        match assert_tx_succeeded_accepted(
-            &Felt::from_hex_unchecked(tx["receipt"]["transaction_hash"].as_str().unwrap()),
-            &devnet.json_rpc_client,
-        )
-        .await
-        {
-            Ok(_) => {}
-            Err(e) => anyhow::bail!("Transaction failed: {}", e),
-        };
+        assert_tx_succeeded_accepted(
+            &Felt::from_hex_unchecked(tx["receipt"]["transaction_hash"].as_str().unwrap()),
+            &devnet.json_rpc_client,
+        )
+        .await?;
     }
 
     Ok(())
 }

249-252: Avoid usize math/casts when building Felt.

Prefer u128 to prevent platform-dependent usize casts.

-    assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::PreConfirmed)
+    assert_balance(
+        &devnet,
+        Felt::from((tx_count as u128) * DUMMY_AMOUNT),
+        BlockTag::PreConfirmed,
+    )
         .await;
-    assert_balance(&devnet, Felt::ZERO, BlockTag::Latest).await;
+    assert_balance(&devnet, Felt::ZERO, BlockTag::Latest).await;
-    assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::PreConfirmed)
+    assert_balance(
+        &devnet,
+        Felt::from((tx_count as u128) * DUMMY_AMOUNT),
+        BlockTag::PreConfirmed,
+    )
         .await;
-    assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::Latest).await;
+    assert_balance(
+        &devnet,
+        Felt::from((tx_count as u128) * DUMMY_AMOUNT),
+        BlockTag::Latest,
+    )
+    .await;

Also applies to: 264-267

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94abc60 and f000fe6.

📒 Files selected for processing (29)
  • tests/integration/common/utils.rs (8 hunks)
  • tests/integration/get_transaction_by_hash.rs (3 hunks)
  • tests/integration/get_transaction_receipt_by_hash.rs (1 hunks)
  • tests/integration/test_abort_blocks.rs (3 hunks)
  • tests/integration/test_accepting_blocks_on_l1.rs (5 hunks)
  • tests/integration/test_account_impersonation.rs (2 hunks)
  • tests/integration/test_account_selection.rs (7 hunks)
  • tests/integration/test_advancing_time.rs (2 hunks)
  • tests/integration/test_advancing_time_on_fork.rs (2 hunks)
  • tests/integration/test_blocks_generation.rs (10 hunks)
  • tests/integration/test_call.rs (1 hunks)
  • tests/integration/test_deploy.rs (4 hunks)
  • tests/integration/test_estimate_fee.rs (7 hunks)
  • tests/integration/test_fork.rs (5 hunks)
  • tests/integration/test_gas_modification.rs (1 hunks)
  • tests/integration/test_messaging.rs (6 hunks)
  • tests/integration/test_old_state.rs (2 hunks)
  • tests/integration/test_restart.rs (1 hunks)
  • tests/integration/test_simulate_transactions.rs (5 hunks)
  • tests/integration/test_subscription_to_blocks.rs (7 hunks)
  • tests/integration/test_subscription_to_events.rs (14 hunks)
  • tests/integration/test_subscription_to_new_tx_receipts.rs (16 hunks)
  • tests/integration/test_subscription_to_new_txs.rs (15 hunks)
  • tests/integration/test_subscription_to_reorg.rs (2 hunks)
  • tests/integration/test_subscription_to_tx_status.rs (13 hunks)
  • tests/integration/test_subscription_with_invalid_block_id.rs (2 hunks)
  • tests/integration/test_transaction_handling.rs (2 hunks)
  • tests/integration/test_v3_transactions.rs (5 hunks)
  • tests/integration/test_websocket.rs (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-16T07:35:24.725Z
Learnt from: FabijanC
PR: 0xSpaceShard/starknet-devnet#799
File: crates/starknet-devnet-core/src/starknet/mod.rs:240-246
Timestamp: 2025-06-16T07:35:24.725Z
Learning: In the Starknet devnet codebase, `get_class_hash_at` has different behaviors at different layers: at the internal state level (blockifier), it returns `Ok(ClassHash(Felt::ZERO))` for undeployed addresses, while at the RPC layer, this gets converted to `Err(Error::ContractNotFound)`. When checking if a contract is deployed at the internal state level, use `is_ok_and(|h| h.0 == Felt::ZERO)` to detect undeployed addresses.

Applied to files:

  • tests/integration/test_old_state.rs
  • tests/integration/test_restart.rs
  • tests/integration/test_v3_transactions.rs
  • tests/integration/test_account_selection.rs
  • tests/integration/test_deploy.rs
🔇 Additional comments (30)
tests/integration/test_websocket.rs (1)

109-109: Unwrapping the Result is correct here

Switching to .await.unwrap() on assert_no_notifications aligns with the new Result-returning helper and will fail the test with a clear message if notifications are present.

tests/integration/test_abort_blocks.rs (2)

21-21: Result-based assertion use looks good

Unwrapping assert_contains propagates failures cleanly and fits the new pattern.


115-116: Correct: assert aborted txs via Result-returning helper

Using .await.unwrap() matches the new API and surfaces assertion failures precisely.

tests/integration/test_fork.rs (4)

264-269: Good migration to Result-returning balance helper

Unwrapping get_contract_balance is consistent with the refactor.


295-298: Explicit success assertion now propagates errors properly

assert_tx_succeeded_accepted(...).await.unwrap(); is the right replacement for panic-based assertions.


300-304: Consistent use of Result-returning balance helper

Same comment as above—LGTM.


576-581: Good: propagate all substring checks via Result

The chained assert_contains(...).unwrap() calls make failures explicit and keep messages informative.

tests/integration/test_v3_transactions.rs (4)

75-77: LGTM: explicit unwrap on Result-returning assertion

This matches the new helper semantics and improves failure locality.


105-107: LGTM: deploy success assertion now returns Result

Good usage of .await.unwrap() to fail fast when deployment isn’t accepted.


158-160: LGTM: unwrap after balance-affecting transfer assertion

Consistent with the refactor.


406-407: LGTM: substring check uses Result-returning helper

Clearer failure message on revert reason mismatch.

tests/integration/common/utils.rs (5)

20-21: Importing TransactionReceipt is correct

The typed receipt usage below relies on this; good addition.


139-145: Good API: balance helper returns Result

This aligns with the PR goal and keeps call sites consistent.


509-513: Good: convert Message error extraction to Result

Clear, idiomatic use of bail! for unexpected variants.


516-523: Good: convert Nested error extraction to Result

Consistent with the error-propagation approach.


708-718: LGTM: assert_contains now returns Result with a helpful diff

Great error message; keeps both pattern and text in context.

tests/integration/test_restart.rs (1)

96-99: LGTM: unwrap adopted for Result-based assertion helper.

Change improves error pinpointing without altering test intent.

tests/integration/get_transaction_receipt_by_hash.rs (1)

245-245: LGTM: explicit unwrap on substring assertion.

Keeps failure localized while retaining flexible substring matching.

tests/integration/test_gas_modification.rs (1)

410-412: LGTM: explicit unwrap surfaces the failing line in stack traces.

Switch assert_tx_succeeded_accepted to .await.unwrap() — aligned with the Result‑returning helpers goal. Automated verification couldn't confirm other helpers (ripgrep skipped files / PCRE2 errors); run the supplied check locally or confirm all listed async/sync helpers consistently use .await.unwrap()/.expect()/? or propagate Result.

tests/integration/test_transaction_handling.rs (1)

48-49: LGTM: unwrap updated Result-based assertions.

The .unwrap() additions align with the new helper signatures and keep failures localized.

Also applies to: 126-127

tests/integration/test_old_state.rs (1)

216-239: LGTM: explicit unwrap on nested-error/message extraction.

Consistent with Result-based helpers and keeps stack traces actionable.

tests/integration/get_transaction_by_hash.rs (1)

41-44: LGTM: assert_tx_succeeded_accepted now unwrapped.

Matches the Result-based helper contract; keeps tests straightforward.

Also applies to: 71-74, 103-106

tests/integration/test_subscription_to_blocks.rs (1)

47-47: LGTM: unwrapping no-notification assertions.

Consistent with the suite-wide change. Keep the two commented flaky assertions as-is until CI stabilizes.

Also applies to: 92-92, 119-119, 141-141, 145-145, 180-180, 216-216, 231-231

tests/integration/test_simulate_transactions.rs (1)

489-489: LGTM: assert_contains unwrapped in simulation negative-path assertions

These negative-path assertions unwrap message text and are brittle; match on stable substrings or error codes where possible.

Verification: the ripgrep scan returned "No files were searched" (ripgrep skipped files) — cannot confirm no remaining helper uses. Re-run locally with:

rg -nP -S --hidden -g '!target/**' -C1 '\b(assert_contains|assert_no_notifications|assert_tx_succeeded_accepted|extract_(nested|message)_error)\s*\('

Applies to tests/integration/test_simulate_transactions.rs lines 489, 581, 713, 940, 1121.

tests/integration/test_estimate_fee.rs (2)

102-104: LGTM: asserting txs succeeded now properly propagates errors via .await.unwrap().

Also applies to: 205-207


642-643: Message substring checks now propagate failures — OK.

These are fine; if messages become unstable across deps, consider constants or pattern-based checks.

Also applies to: 736-737

tests/integration/test_messaging.rs (2)

307-307: LGTM: success assertion now fails fast via .await.unwrap().


441-441: LGTM: error-message assertions now propagate via .unwrap().

Also applies to: 453-453

tests/integration/test_blocks_generation.rs (2)

60-61: LGTM on updated unwraps and Result-based helpers usage.

Call-site adjustments are consistent with the PR direction.

If CI lints for unused_must_use, please re-run after applying the pre_confirmed unwrap fix above.

Also applies to: 86-87, 230-231, 259-260, 275-275, 458-468, 354-357


70-71: No change required — helper returns () (no Result to unwrap).
assert_tx_succeeded_pre_confirmed is defined as pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(_tx_hash: &Felt, _client: &T) in tests/integration/common/utils.rs:125 and returns unit, so awaiting it without unwrap/expect is correct.

Likely an incorrect or invalid review comment.

@Abeeujah Abeeujah requested a review from FabijanC September 16, 2025 08:55
Copy link
Contributor

@FabijanC FabijanC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still many helper functions containing macro assertions. In case of error/failed assertion, this has the same effect as panicking.

Also, coderabbit left some useful nitpick comments.

Comment on lines 109 to 112
let receipt = match client.get_transaction_receipt(tx_hash).await {
Ok(receipt) => receipt.receipt,
Err(err) => return Err(err.into()),
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be replaced with this?

Suggested change
let receipt = match client.get_transaction_receipt(tx_hash).await {
Ok(receipt) => receipt.receipt,
Err(err) => return Err(err.into()),
};
let receipt = client.get_transaction_receipt(tx_hash).await?.receipt;

Comment on lines 170 to 173
let receipt: TransactionReceipt = match client.get_transaction_receipt(tx_hash).await {
Ok(receipt) => receipt.receipt,
Err(e) => return Err(e.into()),
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be replaced with this?

Suggested change
let receipt: TransactionReceipt = match client.get_transaction_receipt(tx_hash).await {
Ok(receipt) => receipt.receipt,
Err(e) => return Err(e.into()),
};
let receipt = client.get_transaction_receipt(tx_hash).await?.receipt;

Comment on lines 157 to 161
Ok(res) => {
assert_eq!(res.len(), 1);
res[0]
Ok(res[0])
}
Err(e) => panic!("Call failed: {e}"),
Err(e) => anyhow::bail!("Call failed: {e}"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping assertions in these helper functions does not completely address the issue targeted by this PR. Perhaps it can be restructured as:

        Ok(res) if res.len() == 1 => Ok(res[0]),
        other => anyhow::bail!("Expected a single-felt result; got: {other:?}"),

Comment on lines 178 to 181
match assert_contains(reason, expected_reason) {
Ok(_) => (),
Err(e) => return Err(e),
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be replaced with this?

Suggested change
match assert_contains(reason, expected_reason) {
Ok(_) => (),
Err(e) => return Err(e),
}
assert_contains(reason, expected_reason)?;

Comment on lines 461 to 472
assert_eq!(iterable1.len(), iterable2.len());
match iterable1.len() == iterable2.len() {
true => (),
false => return Err(anyhow::anyhow!("Length mismatch")),
}
for e in iterable1 {
assert!(iterable2.contains(e));
match iterable2.contains(e) {
true => (),
false => return Err(anyhow::anyhow!("Element mismatch")),
}
}
Ok(())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO matching with cases true and false is rarely acceptable. And keeping the length assertion as the first line of the function makes no sense.

Suggested change
assert_eq!(iterable1.len(), iterable2.len());
match iterable1.len() == iterable2.len() {
true => (),
false => return Err(anyhow::anyhow!("Length mismatch")),
}
for e in iterable1 {
assert!(iterable2.contains(e));
match iterable2.contains(e) {
true => (),
false => return Err(anyhow::anyhow!("Element mismatch")),
}
}
Ok(())
if iterable1.len() != iterable2.len() {
anyhow::bail!(
"Length mismatch: left = {}, right = {}",
iterable1.len(),
iterable2.len()
);
}
for e in iterable1 {
if !iterable2.contains(e) {
anyhow::bail!("Element {:?} from left not found in right", e);
}
}
Ok(())

Comment on lines 617 to 620
Err(e) if e.to_string().contains("deadline has elapsed") => {
/* expected */
Ok(())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Err(e) if e.to_string().contains("deadline has elapsed") => {
/* expected */
Ok(())
}
Err(e) if e.to_string().contains("deadline has elapsed") => Ok(()), // expected

Comment on lines 152 to 160
match assert_tx_succeeded_accepted(
&account_deployment.transaction_hash,
&devnet.json_rpc_client,
)
.await
{
Ok(_) => (),
Err(e) => panic!("Transaction failed: {}", e),
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not unwrap?

Comment on lines 115 to 124
match assert_tx_succeeded_accepted(
&Felt::from_hex_unchecked(tx["receipt"]["transaction_hash"].as_str().unwrap()),
&devnet.json_rpc_client,
)
.await;
.await
{
Ok(_) => {}
Err(e) => anyhow::bail!("Transaction failed: {}", e),
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use .map_err(...)?

@@ -112,12 +112,18 @@ async fn assert_latest_block_with_receipts(
assert_eq!(latest_block["status"], "ACCEPTED_ON_L2");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The abundance of assertions remaining in helper functions might be addressed with anyhow::ensure, as suggested by coderabbit in one of its nitpick comments.

@Abeeujah
Copy link
Contributor Author

@coderabbitai review

Copy link

coderabbitai bot commented Sep 17, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/integration/test_trace.rs (1)

24-60: Remove remaining unwraps to fully eliminate panics in the assertion helper.

Both validate_invocation and fee_transfer_invocation are Options; unwrap can still panic.

-            for invocation in [validate_invocation.as_ref().unwrap(), execute_invocation] {
+            let validate = validate_invocation
+                .as_ref()
+                .ok_or_else(|| anyhow::anyhow!("missing validate_invocation"))?;
+            for invocation in [validate, execute_invocation] {
                 anyhow::ensure!(
                     invocation.contract_address
                         == Felt::from_hex_unchecked(CHARGEABLE_ACCOUNT_ADDRESS)
                 );
                 anyhow::ensure!(
                     invocation.calldata
                         == vec![
                         // ...
                         ]
                 );
             }
-
-            anyhow::ensure!(
-                fee_transfer_invocation.as_ref().unwrap().contract_address
-                    == STRK_ERC20_CONTRACT_ADDRESS
-            );
+            let fee = fee_transfer_invocation
+                .as_ref()
+                .ok_or_else(|| anyhow::anyhow!("missing fee_transfer_invocation"))?;
+            anyhow::ensure!(fee.contract_address == STRK_ERC20_CONTRACT_ADDRESS);
♻️ Duplicate comments (2)
tests/integration/common/utils.rs (1)

443-458: Consider set-based comparison for large inputs.

Current O(n²) search is fine for small test slices; if inputs can grow, prefer HashSet to avoid quadratic cost. Requires T: Eq + Hash.

Example:

use std::collections::HashSet;
anyhow::ensure!(HashSet::<&T>::from_iter(iterable1) == HashSet::<&T>::from_iter(iterable2));
tests/integration/test_messaging.rs (1)

982-987: Re: borrowing from temporary JSON — this pattern is valid

Referencing a field of a temporary serde_json::Value is OK here due to Rust’s temporary lifetime extension. Previous warning was incorrect.

🧹 Nitpick comments (20)
tests/integration/common/errors.rs (1)

52-59: Nice: RpcError now implements Error; consider improving Display output.

Current Display prints Debug, which is noisy in logs. Prefer a concise "code:message" format.

 impl fmt::Display for RpcError {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{self:?}")
+        match &self.data {
+            Some(data) => write!(f, "RPC error {}: {} | data={}", self.code, self.message, data),
+            None => write!(f, "RPC error {}: {}", self.code, self.message),
+        }
     }
 }
tests/integration/test_dump_and_load.rs (1)

22-39: Avoid unwrap in Result-returning helper; propagate with ?

This aligns with the PR’s goal (no panics in helpers).

-        devnet_dump.create_block().await.unwrap();
+        devnet_dump.create_block().await?;
tests/integration/test_advancing_time.rs (1)

33-38: Prevent potential u64 overflow when computing upper_limit.

Use checked_add (error) or saturating_add (clamp) instead of plain +.

 pub fn assert_ge_with_buffer(val1: u64, val2: u64) -> Result<(), anyhow::Error> {
     anyhow::ensure!(val1 >= val2, "Failed inequation: {val1:?} >= {val2:?}");
-    let upper_limit = val2 + BUFFER_TIME_SECONDS;
+    let upper_limit = val2
+        .checked_add(BUFFER_TIME_SECONDS)
+        .ok_or_else(|| anyhow::anyhow!("timestamp overflow computing upper limit"))?;
     anyhow::ensure!(val1 <= upper_limit, "Failed inequation: {val1:?} <= {upper_limit:?}");
     Ok(())
 }
 
 pub fn assert_gt_with_buffer(val1: u64, val2: u64) -> Result<(), anyhow::Error> {
     anyhow::ensure!(val1 > val2, "Failed inequation: {val1:?} > {val2:?}");
-    let upper_limit = val2 + BUFFER_TIME_SECONDS;
+    let upper_limit = val2
+        .checked_add(BUFFER_TIME_SECONDS)
+        .ok_or_else(|| anyhow::anyhow!("timestamp overflow computing upper limit"))?;
     anyhow::ensure!(val1 <= upper_limit, "Failed inequation: {val1:?} <= {upper_limit:?}");
     Ok(())
 }

Also applies to: 40-45

tests/integration/test_account_selection.rs (1)

44-65: Add messages to ensure! checks for clearer failures.

Current ensures are silent on mismatches; include expected vs. actual.

-    anyhow::ensure!(retrieved_class_hash == expected_hash);
+    anyhow::ensure!(
+        retrieved_class_hash == expected_hash,
+        "unexpected class hash: got {retrieved_class_hash:#x}, expected {expected_hash:#x}"
+    );
@@
-    anyhow::ensure!(Felt::from_hex_unchecked(config_class_hash_hex) == expected_hash);
+    anyhow::ensure!(
+        Felt::from_hex_unchecked(config_class_hash_hex) == expected_hash,
+        "config.account_contract_class_hash mismatch: got {config_class_hash_hex}, expected {expected_hash_hex}"
+    );
tests/integration/common/utils.rs (5)

122-134: Convert pre-confirmed helper to Result for consistency.

Keep it feature‑gated or stubbed, but make the signature return Result to align with this PR’s goal and call‑sites.

Apply this minimal stub until starknet-rs 0.17 is adopted:

-pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(_tx_hash: &Felt, _client: &T) {
-    eprintln!("TODO: Currently suppressed; undo when starknet-rs updated to 0.17");
+pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(
+    _tx_hash: &Felt,
+    _client: &T,
+) -> Result<(), anyhow::Error> {
+    // TODO: Re-enable real checks when starknet-rs is updated to 0.17.
+    Ok(())

222-226: Avoid unwrap on ABI extraction.

as_str().unwrap() can panic if ABI isn’t a string; return a structured error instead.

Apply:

 fn take_abi_from_json(value: &mut serde_json::Value) -> Result<serde_json::Value, anyhow::Error> {
     let abi_jsonified = value["abi"].take();
-    anyhow::ensure!(abi_jsonified != serde_json::json!(null));
-    Ok(serde_json::from_str(abi_jsonified.as_str().unwrap())?)
+    anyhow::ensure!(abi_jsonified != serde_json::json!(null), "Missing `abi` field");
+    let abi_str = abi_jsonified
+        .as_str()
+        .ok_or_else(|| anyhow::anyhow!("`abi` field is not a string"))?;
+    Ok(serde_json::from_str(abi_str)?)
 }

470-486: Remove downcast unwrap to avoid panic.

Gracefully handle unexpected provider error types.

-            let impl_specific_error: &JsonRpcClientError<HttpTransportError> =
-                provider_impl_error.as_any().downcast_ref().unwrap();
-            match impl_specific_error {
+            if let Some(impl_specific_error) =
+                provider_impl_error.as_any().downcast_ref::<JsonRpcClientError<HttpTransportError>>()
+            {
+                match impl_specific_error {
                 JsonRpcClientError::JsonRpcError(json_rpc_error) => Ok(json_rpc_error.clone()),
-                other => {
-                    Err(anyhow::Error::msg(format!("Cannot extract RPC error from: {:?}", other)))
-                }
-            }
+                other => Err(anyhow::anyhow!("Cannot extract RPC error from: {other:?}")),
+                }
+            } else {
+                Err(anyhow::anyhow!("Provider error is not JsonRpcClientError; got: {provider_impl_error:?}"))
+            }

588-597: Add context to ensures for easier diffs.

Including expected/actual in error text speeds up debugging.

-    anyhow::ensure!(notification["jsonrpc"] == "2.0");
-    anyhow::ensure!(notification["method"] == method);
+    anyhow::ensure!(notification["jsonrpc"] == "2.0", "jsonrpc != 2.0: {notification}");
+    anyhow::ensure!(notification["method"] == method, "method mismatch: {notification}");

601-609: Match timeout by type, not substring.

String-matching “deadline has elapsed” is brittle; downcast to tokio timeout error.

 match receive_rpc_via_ws(ws).await {
-    Ok(resp) => anyhow::bail!("Expected no notifications; found: {resp}"),
-    Err(e) if e.to_string().contains("deadline has elapsed") => Ok(()), /* expected */
+    Ok(resp) => anyhow::bail!("Expected no notifications; found: {resp}"),
+    Err(e) if e.downcast_ref::<tokio::time::error::Elapsed>().is_some() => Ok(()), /* expected */
     Err(e) => anyhow::bail!("Expected to error out due to empty channel; found: {e}"),
 }
tests/integration/test_subscription_to_tx_status.rs (2)

34-54: Prefer field-wise checks over whole-JSON equality.

Object order or unrelated fields could cause fragile failures; assert specific fields instead.

-    anyhow::ensure!(
-        notification
-            == json!({
-                "jsonrpc": "2.0",
-                "method": "starknet_subscriptionTransactionStatus",
-                "params": {
-                    "result": {
-                        "transaction_hash": tx_hash,
-                        "status": {
-                            "finality_status": expected_finality_status,
-                            "failure_reason": null,
-                            "execution_status": "SUCCEEDED",
-                        },
-                    },
-                    "subscription_id": subscription_id,
-                }
-            })
-    );
+    anyhow::ensure!(notification["jsonrpc"] == "2.0", "bad jsonrpc: {notification}");
+    anyhow::ensure!(notification["method"] == "starknet_subscriptionTransactionStatus", "bad method: {notification}");
+    anyhow::ensure!(notification["params"]["subscription_id"] == subscription_id, "bad sub id: {notification}");
+    let result = &notification["params"]["result"];
+    anyhow::ensure!(result["transaction_hash"] == tx_hash, "bad tx hash: {notification}");
+    anyhow::ensure!(result["status"]["execution_status"] == "SUCCEEDED", "bad exec status: {notification}");
+    anyhow::ensure!(result["status"]["finality_status"] == expected_finality_status, "bad finality: {notification}");
+    anyhow::ensure!(result["status"]["failure_reason"].is_null(), "unexpected failure_reason: {notification}");

141-179: Use ? instead of unwrap() inside Result-returning helper.

Keep propagation consistent and avoid test panics.

-        let notification = receive_rpc_via_ws(ws_before_tx).await.unwrap();
+        let notification = receive_rpc_via_ws(ws_before_tx).await?;
@@
-        let notification = receive_rpc_via_ws(ws_after_tx).await?;
+        let notification = receive_rpc_via_ws(ws_after_tx).await?;
tests/integration/test_gas_modification.rs (2)

174-184: Avoid float-to-integer casts for big constants.

8.5e18 as u128 loses precision; use integer literals.

-    let l1_fri_price = 8.5e18 as u128;
-    let l1_data_fri_price = 7.5e18 as u128;
-    let l2_fri_price = 6.5e18 as u128;
+    let l1_fri_price: u128 = 8_500_000_000_000_000_000;
+    let l1_data_fri_price: u128 = 7_500_000_000_000_000_000;
+    let l2_fri_price: u128 = 6_500_000_000_000_000_000;
@@
-        "gas_price_wei": 9e18 as u128,
+        "gas_price_wei": 9_000_000_000_000_000_000_u128,
@@
-        "data_gas_price_wei": 8e18 as u128,
+        "data_gas_price_wei": 8_000_000_000_000_000_000_u128,
@@
-        "l2_gas_price_wei": 7e18 as u128,
+        "l2_gas_price_wei": 7_000_000_000_000_000_000_u128,
@@
-    let new_l2_fri_price = 9e7 as u128; // approximate upper limit that will pass
+    let new_l2_fri_price: u128 = 90_000_000; // approximate upper limit that will pass
@@
-        "gas_price_wei": 9e18 as u128,
-        "data_gas_price_wei": 8e18 as u128,
-        "l2_gas_price_wei": 7.5e18 as u128,
-        "gas_price_fri": 7e18 as u128,
-        "data_gas_price_fri": 6e18 as u128,
-        "l2_gas_price_fri": 5.5e18 as u128,
+        "gas_price_wei": 9_000_000_000_000_000_000_u128,
+        "data_gas_price_wei": 8_000_000_000_000_000_000_u128,
+        "l2_gas_price_wei": 7_500_000_000_000_000_000_u128,
+        "gas_price_fri": 7_000_000_000_000_000_000_u128,
+        "data_gas_price_fri": 6_000_000_000_000_000_000_u128,
+        "l2_gas_price_fri": 5_500_000_000_000_000_000_u128,

Also applies to: 189-201, 203-217, 448-455


193-201: Optional: validate response shape before indexing [0].

Safer to check array length and bail with a clear message.

let resp = devnet.send_custom_rpc("starknet_simulateTransactions", params).await?;
let first = resp.get(0).ok_or_else(|| anyhow::anyhow!("empty simulateTransactions response"))?;

Also applies to: 203-217

tests/integration/test_v3_transactions.rs (1)

321-343: Eliminate unwraps inside Result-returning helper; propagate with ?.

Prevents accidental panics if callers change Action/options.

-        .await
-        .map_err(|e| anyhow::Error::msg(e.to_string()))?,
+        .await
+        .map_err(|e| anyhow::Error::msg(e.to_string()))?,
@@
-                    DeclarationV3::new(sierra_class.clone(), *casm_hash, account.unwrap())
+                    DeclarationV3::new(
+                        sierra_class.clone(),
+                        *casm_hash,
+                        account.ok_or(anyhow::anyhow!("Account not found"))?,
+                    )
@@
-                match declaration.send().await.unwrap_err() {
+                match declaration.send().await.unwrap_err() {
@@
-                let account_deployment = AccountDeploymentV3::new(*salt, account_factory.unwrap())
+                let account_deployment = AccountDeploymentV3::new(
+                    *salt,
+                    account_factory.ok_or(anyhow::anyhow!("Account Factory is None"))?,
+                )
@@
-                let execution = ExecutionV3::new(calls.clone(), account.unwrap())
+                let execution = ExecutionV3::new(
+                    calls.clone(),
+                    account.ok_or(anyhow::anyhow!("Account not found"))?,
+                )
@@
-                                assert_contains(reason.as_str(), "Insufficient max L2Gas").unwrap();
+                                assert_contains(reason.as_str(), "Insufficient max L2Gas")?;

Also applies to: 358-373, 375-390, 392-399, 401-423, 427-428

tests/integration/test_abort_blocks.rs (2)

24-38: Make “block not found” assertion robust (avoid brittle full-equality on RpcError)

Comparing whole RpcError (including exact message and data) is fragile. Match code and check substring on message instead.

-    anyhow::ensure!(err == RpcError { code: 24, message: "Block not found".into(), data: None });
-    Ok(())
+    assert_contains(&err.message, "Block not found")?;
+    anyhow::ensure!(err.code == 24);
+    Ok(())

40-51: Tighten match: handle Ok separately for clearer failures

Currently Ok() and unexpected Err() are merged. Split them for sharper diagnostics.

-    for tx_hash in tx_hashes {
-        match devnet.json_rpc_client.get_transaction_by_hash(tx_hash).await {
-            Err(ProviderError::StarknetError(StarknetError::TransactionHashNotFound)) => (),
-            other => anyhow::bail!("Unexpected tx response for {tx_hash:#x}: {other:?}"),
-        }
-    }
+    for tx_hash in tx_hashes {
+        match devnet.json_rpc_client.get_transaction_by_hash(tx_hash).await {
+            Err(ProviderError::StarknetError(StarknetError::TransactionHashNotFound)) => {}
+            Ok(_) => anyhow::bail!("Unexpectedly found tx {tx_hash:#x}"),
+            Err(e) => anyhow::bail!("Unexpected error for {tx_hash:#x}: {e:?}"),
+        }
+    }
tests/integration/test_minting.rs (1)

11-48: Avoid unwraps in Result-returning helper; harden tx_hash check

Use ? instead of unwrap and validate tx_hash presence before starts_with.

-    let tx_hash_value = resp_body["tx_hash"].take();
-    anyhow::ensure!(tx_hash_value.as_str().unwrap().starts_with("0x"));
+    let tx_hash_value = resp_body["tx_hash"].take();
+    let tx_hash_str = tx_hash_value
+        .as_str()
+        .ok_or_else(|| anyhow::anyhow!("tx_hash missing or not a string"))?;
+    anyhow::ensure!(tx_hash_str.starts_with("0x"));
@@
-    let new_balance =
-        devnet.get_balance_latest(&Felt::from_hex_unchecked(address), unit).await.unwrap();
+    let new_balance =
+        devnet.get_balance_latest(&Felt::from_hex_unchecked(address), unit).await?;
tests/integration/test_accepting_blocks_on_l1.rs (1)

31-37: Prefer ? over unwrap() in helper to preserve error context

This helper already returns Result; propagate provider errors directly.

-    for tx_hash in tx_hashes {
-        let tx_status = devnet.json_rpc_client.get_transaction_status(tx_hash).await.unwrap();
-        anyhow::ensure!(tx_status.finality_status() == SequencerTransactionStatus::AcceptedOnL1);
-    }
+    for tx_hash in tx_hashes {
+        let tx_status = devnet.json_rpc_client.get_transaction_status(tx_hash).await?;
+        anyhow::ensure!(tx_status.finality_status() == SequencerTransactionStatus::AcceptedOnL1);
+    }
tests/integration/test_messaging.rs (1)

196-225: Don’t unwrap() inside a Result-returning helper

Propagate the balance query error with ?.

-    anyhow::ensure!(get_balance(devnet, from_address, user).await.unwrap() == [Felt::ZERO]);
+    anyhow::ensure!(get_balance(devnet, from_address, user).await? == [Felt::ZERO]);
tests/integration/test_blocks_generation.rs (1)

271-275: Avoid architecture-dependent arithmetic with usize when creating Felts

Use explicit u128 to prevent surprises across platforms.

-    assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::PreConfirmed)
+    assert_balance(&devnet, Felt::from((tx_count as u128) * DUMMY_AMOUNT), BlockTag::PreConfirmed)
@@
-    assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::PreConfirmed)
+    assert_balance(&devnet, Felt::from((tx_count as u128) * DUMMY_AMOUNT), BlockTag::PreConfirmed)
@@
-    assert_balance(&devnet, Felt::from(tx_count * DUMMY_AMOUNT as usize), BlockTag::Latest)
+    assert_balance(&devnet, Felt::from((tx_count as u128) * DUMMY_AMOUNT), BlockTag::Latest)

Also applies to: 287-293

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f000fe6 and e16a2b8.

📒 Files selected for processing (16)
  • tests/integration/common/errors.rs (1 hunks)
  • tests/integration/common/utils.rs (10 hunks)
  • tests/integration/general_rpc_tests.rs (1 hunks)
  • tests/integration/test_abort_blocks.rs (11 hunks)
  • tests/integration/test_accepting_blocks_on_l1.rs (5 hunks)
  • tests/integration/test_account_selection.rs (8 hunks)
  • tests/integration/test_advancing_time.rs (20 hunks)
  • tests/integration/test_blocks_generation.rs (12 hunks)
  • tests/integration/test_dump_and_load.rs (4 hunks)
  • tests/integration/test_estimate_fee.rs (7 hunks)
  • tests/integration/test_gas_modification.rs (7 hunks)
  • tests/integration/test_messaging.rs (37 hunks)
  • tests/integration/test_minting.rs (4 hunks)
  • tests/integration/test_subscription_to_tx_status.rs (15 hunks)
  • tests/integration/test_trace.rs (5 hunks)
  • tests/integration/test_v3_transactions.rs (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/integration/test_estimate_fee.rs
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-16T07:35:24.725Z
Learnt from: FabijanC
PR: 0xSpaceShard/starknet-devnet#799
File: crates/starknet-devnet-core/src/starknet/mod.rs:240-246
Timestamp: 2025-06-16T07:35:24.725Z
Learning: In the Starknet devnet codebase, `get_class_hash_at` has different behaviors at different layers: at the internal state level (blockifier), it returns `Ok(ClassHash(Felt::ZERO))` for undeployed addresses, while at the RPC layer, this gets converted to `Err(Error::ContractNotFound)`. When checking if a contract is deployed at the internal state level, use `is_ok_and(|h| h.0 == Felt::ZERO)` to detect undeployed addresses.

Applied to files:

  • tests/integration/test_account_selection.rs
  • tests/integration/test_v3_transactions.rs
  • tests/integration/test_blocks_generation.rs
  • tests/integration/common/utils.rs
📚 Learning: 2025-09-16T08:50:01.391Z
Learnt from: Abeeujah
PR: 0xSpaceShard/starknet-devnet#848
File: tests/integration/test_messaging.rs:960-964
Timestamp: 2025-09-16T08:50:01.391Z
Learning: In Rust, when binding a reference to a field or method result of a temporary value (like `let ref_var = &temporary.field` or `let ref_var = &temporary["key"]`), the temporary's lifetime is extended to match the lifetime of the reference binding. This prevents dangling references and makes patterns like `let generated_l2_txs_raw = &devnet.send_custom_rpc(...).await.unwrap()["generated_l2_transactions"];` perfectly valid.

Applied to files:

  • tests/integration/test_messaging.rs
🔇 Additional comments (22)
tests/integration/common/utils.rs (10)

105-120: LGTM: Result-based success assertion is solid.

Typed receipt + explicit checks read well and improve failure messages.


136-141: LGTM: balance helper now propagates errors.


143-157: LGTM: precise matching on single felt.

Clear error message; good use of bail.


159-175: LGTM: revert assertion now error-driven.

Use of TransactionReceipt and chaining assert_contains is clean.


240-244: LGTM: JSON-equality assertions with anyhow::ensure.


438-441: LGTM: propagate deployment error with ?.


488-494: LGTM: equality check now returns Result.


496-502: LGTM: message extraction with precise errors.


504-512: LGTM: nested error extraction with precise errors.


693-704: LGTM: assert_contains now returns Result with rich message.

tests/integration/general_rpc_tests.rs (1)

91-92: LGTM: unwrap the Result from equality assertion.

Matches the new Result-returning helper.

tests/integration/test_subscription_to_tx_status.rs (1)

85-87: LGTM: updated call-sites now unwrap Result-returning helpers.

Also applies to: 95-96, 111-112, 126-127, 137-138, 162-163, 175-176, 212-214, 233-234, 237-238, 260-262, 281-282, 329-329

tests/integration/test_gas_modification.rs (3)

57-61: LGTM: scenario now propagates errors.


119-141: LGTM: clearer ensures with mapped errors.

Also applies to: 151-165


233-236: LGTM: call-sites updated to unwrap scenario errors in tests.

Also applies to: 245-247

tests/integration/test_v3_transactions.rs (2)

74-76: LGTM: assertion helpers now awaited and unwrapped.

Also applies to: 104-107, 157-160, 308-309


186-201: LGTM: tests updated to propagate errors via unwrap on awaited helpers.

Also applies to: 219-225, 286-287

tests/integration/test_abort_blocks.rs (1)

12-22: LGTM: helper now returns Result and propagates via anyhow

The conversion to Result with delegated string-contains check via assert_contains is clean.

tests/integration/test_accepting_blocks_on_l1.rs (1)

39-49: LGTM: assert_latest_accepted_on_l2 now cleanly propagates errors

Consistent use of ? + anyhow::ensure! looks good.

tests/integration/test_messaging.rs (2)

67-89: LGTM: increase_balance returns Result and maps account error

The error mapping is sufficient for test diagnostics.


92-105: LGTM: get_balance now returns Result and uses typed call

Clear and idiomatic.

tests/integration/test_blocks_generation.rs (1)

113-139: LGTM: receipts helper uses JSON path checks with explicit error messages

The guarded hex parsing with ok_or(anyhow!(...))? is solid.

Comment on lines 65 to 78
async fn assert_pre_confirmed_block_with_tx_hashes(
devnet: &BackgroundDevnet,
tx_count: usize,
) -> Result<(), anyhow::Error> {
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_tx_hashes().await?;

assert_eq!(pre_confirmed_block.transactions.len(), tx_count);
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count);

for tx_hash in pre_confirmed_block.transactions {
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await;
}

Ok(())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: dropped Result from assert_tx_succeeded_pre_confirmed (error silently ignored)

These helpers now return Result; use ? to fail the helper on error.

-    for tx_hash in pre_confirmed_block.transactions {
-        assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await;
-    }
+    for tx_hash in pre_confirmed_block.transactions {
+        assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await?;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async fn assert_pre_confirmed_block_with_tx_hashes(
devnet: &BackgroundDevnet,
tx_count: usize,
) -> Result<(), anyhow::Error> {
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_tx_hashes().await?;
assert_eq!(pre_confirmed_block.transactions.len(), tx_count);
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count);
for tx_hash in pre_confirmed_block.transactions {
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await;
}
Ok(())
}
async fn assert_pre_confirmed_block_with_tx_hashes(
devnet: &BackgroundDevnet,
tx_count: usize,
) -> Result<(), anyhow::Error> {
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_tx_hashes().await?;
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count);
for tx_hash in pre_confirmed_block.transactions {
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await?;
}
Ok(())
}
🤖 Prompt for AI Agents
In tests/integration/test_blocks_generation.rs around lines 65 to 78, the helper
assert_tx_succeeded_pre_confirmed now returns a Result but its return value is
being ignored; update the loop to propagate errors by awaiting with ? so the
test fails on error. Replace the current for loop body with a call that uses
.await? (e.g., for tx_hash in pre_confirmed_block.transactions {
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await?; }),
leaving the function signature returning Result<(), anyhow::Error> intact.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(_tx_hash: &Felt, _client: &T) {} doesn't return a result

Comment on lines 98 to 111
async fn assert_pre_confirmed_block_with_txs(
devnet: &BackgroundDevnet,
tx_count: usize,
) -> Result<(), anyhow::Error> {
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_txs().await?;

assert_eq!(pre_confirmed_block.transactions.len(), tx_count);
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count);

for tx in pre_confirmed_block.transactions {
assert_tx_succeeded_pre_confirmed(tx.transaction_hash(), &devnet.json_rpc_client).await;
}

Ok(())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: dropped Result from assert_tx_succeeded_pre_confirmed in txs variant

Same issue; propagate with ?.

-    for tx in pre_confirmed_block.transactions {
-        assert_tx_succeeded_pre_confirmed(tx.transaction_hash(), &devnet.json_rpc_client).await;
-    }
+    for tx in pre_confirmed_block.transactions {
+        assert_tx_succeeded_pre_confirmed(tx.transaction_hash(), &devnet.json_rpc_client).await?;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async fn assert_pre_confirmed_block_with_txs(
devnet: &BackgroundDevnet,
tx_count: usize,
) -> Result<(), anyhow::Error> {
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_txs().await?;
assert_eq!(pre_confirmed_block.transactions.len(), tx_count);
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count);
for tx in pre_confirmed_block.transactions {
assert_tx_succeeded_pre_confirmed(tx.transaction_hash(), &devnet.json_rpc_client).await;
}
Ok(())
}
async fn assert_pre_confirmed_block_with_txs(
devnet: &BackgroundDevnet,
tx_count: usize,
) -> Result<(), anyhow::Error> {
let pre_confirmed_block = devnet.get_pre_confirmed_block_with_txs().await?;
anyhow::ensure!(pre_confirmed_block.transactions.len() == tx_count);
for tx in pre_confirmed_block.transactions {
assert_tx_succeeded_pre_confirmed(tx.transaction_hash(), &devnet.json_rpc_client).await?;
}
Ok(())
}
🤖 Prompt for AI Agents
In tests/integration/test_blocks_generation.rs around lines 98 to 111, the call
to assert_tx_succeeded_pre_confirmed(...).await ignores its Result return value;
change the call to propagate the error by using ? after .await (i.e., await the
future and use ? to return early on error) so the function returns the error to
the caller; no other logic changes required.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as replied above, pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(_tx_hash: &Felt, _client: &T) is yet to be implemented

Comment on lines 141 to 162
async fn assert_pre_confirmed_block_with_receipts(
devnet: &BackgroundDevnet,
tx_count: usize,
) -> Result<(), anyhow::Error> {
let pre_confirmed_block = &devnet
.send_custom_rpc("starknet_getBlockWithReceipts", json!({ "block_id": "pre_confirmed" }))
.await
.unwrap();
.await?;

assert!(pre_confirmed_block["status"].is_null());
assert_eq!(pre_confirmed_block["transactions"].as_array().unwrap().len(), tx_count);
anyhow::ensure!(pre_confirmed_block["status"].is_null());
anyhow::ensure!(pre_confirmed_block["transactions"].as_array().unwrap().len() == tx_count);

for tx in pre_confirmed_block["transactions"].as_array().unwrap() {
let tx_hash = Felt::from_hex_unchecked(tx["receipt"]["transaction_hash"].as_str().unwrap());
let tx_hash = Felt::from_hex_unchecked(
tx["receipt"]["transaction_hash"]
.as_str()
.ok_or(anyhow!("failed to parse transaction hash"))?,
);
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await;
}

Ok(())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: dropped Result from assert_tx_succeeded_pre_confirmed in receipts helper

Propagate the error to avoid false positives.

-        assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await;
+        assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async fn assert_pre_confirmed_block_with_receipts(
devnet: &BackgroundDevnet,
tx_count: usize,
) -> Result<(), anyhow::Error> {
let pre_confirmed_block = &devnet
.send_custom_rpc("starknet_getBlockWithReceipts", json!({ "block_id": "pre_confirmed" }))
.await
.unwrap();
.await?;
assert!(pre_confirmed_block["status"].is_null());
assert_eq!(pre_confirmed_block["transactions"].as_array().unwrap().len(), tx_count);
anyhow::ensure!(pre_confirmed_block["status"].is_null());
anyhow::ensure!(pre_confirmed_block["transactions"].as_array().unwrap().len() == tx_count);
for tx in pre_confirmed_block["transactions"].as_array().unwrap() {
let tx_hash = Felt::from_hex_unchecked(tx["receipt"]["transaction_hash"].as_str().unwrap());
let tx_hash = Felt::from_hex_unchecked(
tx["receipt"]["transaction_hash"]
.as_str()
.ok_or(anyhow!("failed to parse transaction hash"))?,
);
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await;
}
Ok(())
}
async fn assert_pre_confirmed_block_with_receipts(
devnet: &BackgroundDevnet,
tx_count: usize,
) -> Result<(), anyhow::Error> {
let pre_confirmed_block = &devnet
.send_custom_rpc("starknet_getBlockWithReceipts", json!({ "block_id": "pre_confirmed" }))
.await?;
anyhow::ensure!(pre_confirmed_block["status"].is_null());
anyhow::ensure!(pre_confirmed_block["transactions"].as_array().unwrap().len() == tx_count);
for tx in pre_confirmed_block["transactions"].as_array().unwrap() {
let tx_hash = Felt::from_hex_unchecked(
tx["receipt"]["transaction_hash"]
.as_str()
.ok_or(anyhow!("failed to parse transaction hash"))?,
);
assert_tx_succeeded_pre_confirmed(&tx_hash, &devnet.json_rpc_client).await?;
}
Ok(())
}
🤖 Prompt for AI Agents
In tests/integration/test_blocks_generation.rs around lines 141 to 162, the call
to assert_tx_succeeded_pre_confirmed is awaiting but ignoring its Result, which
can hide failures; change the call to propagate the Result (use the ? operator
on the awaited call) so the helper returns an error on failure and the test
doesn't produce false positives.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned again, pub async fn assert_tx_succeeded_pre_confirmed<T: Provider>(_tx_hash: &Felt, _client: &T) is currently unimplemented.

@Abeeujah Abeeujah requested a review from FabijanC September 18, 2025 13:13
@@ -22,28 +22,30 @@ async fn assert_accepted_on_l1(
for block_hash in block_hashes {
match devnet.json_rpc_client.get_block_with_tx_hashes(BlockId::Hash(*block_hash)).await {
Ok(MaybePreConfirmedBlockWithTxHashes::Block(block)) => {
assert_eq!(block.status, BlockStatus::AcceptedOnL1)
anyhow::ensure!(block.status == BlockStatus::AcceptedOnL1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem

I was not aware that anyhow::ensure only accepts a boolean argument. This reduces the information in the reported error. assert_eq nicely reports the failure telling us the left and the right argument which don't match, but the stack trace is messy. With anyhow::ensure we get a nice stack trace, but lose the mismatched content.

Suggestion

The best would be to have a custom equality assertion function that would only be used in these helper functions (basically the places where you've now configured anyhow::ensure). The function would accept two arguments implementing the Debug trait, return Ok(()) if equal, or return an Err containing info on left and right, but propagating it. Though there might be issues when the two arguments are not of the same type, meaning it would require something like this:

fn assert_eq_propagated<T, U>(a: T, b: U) -> Result<(), anyhow::Error>
where
    T: Into<U> + Debug,  // T can be converted into U
    U: From<T> + Debug,   // U can be constructed from T
{

Let me know if you think this is too much of hassle and if it's even feasible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ensure!(user == 0, "only user 0 is allowed"); takes in an optional error message argument, we could use that, but if it don't report as much info as needed, I'd look into implementing your suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FabijanC what do you think about this?

fn take_abi_from_json(value: &mut serde_json::Value) -> Result<serde_json::Value, anyhow::Error> {
    let abi_jsonified = value["abi"].take();
    let json_null = serde_json::json!(null);
    anyhow::ensure!(
        abi_jsonified != json_null,
        format!("assertion `left == right` failed, left: {abi_jsonified}, right: {json_null}")
    );
    Ok(serde_json::from_str(abi_jsonified.as_str().unwrap())?)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Situations where we assert non-equality (i.e. x != y) or "greater-than" relation (i.e. x > y) are less problematic. Personally, I'd rather we kept using assert_eq than anyhow::ensure and now I regret recommending it in the first place. The ideal solution would be to have a macro or a custom function that I proposed above, but I will understand if you don't want to implement one of those.

So: let me know if you will just revert the anyhow::ensure changes or also implement a custom asserter. Sorry once again for the double work on your side.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, It's fine, my bad, I went with providing custom error messages, I'd implement the custom assert, and push.

@Abeeujah
Copy link
Contributor Author

@coderabbitai review

@Abeeujah Abeeujah requested a review from FabijanC September 20, 2025 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Custom assertion methods could benefit from returning Result
2 participants