Skip to content

Use INVOKE as type for deployment txs via UDC#835

Merged
FabijanC merged 3 commits intomainfrom
fix-deploy-receipt
Aug 25, 2025
Merged

Use INVOKE as type for deployment txs via UDC#835
FabijanC merged 3 commits intomainfrom
fix-deploy-receipt

Conversation

@FabijanC
Copy link
Copy Markdown
Contributor

@FabijanC FabijanC commented Aug 22, 2025

Usage related changes

Development related changes

  • This resulted in some unused code, so I removed it.

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

  • New Features

    • Added explicit DeployAccount and L1 Handler transaction receipt types.
    • DeployAccount receipts include the deployed contract address.
    • Improved and consistent finality/status handling across receipt types.
  • Refactor

    • Simplified receipt construction; removed reliance on event scanning for deployed addresses.
    • Invoke transactions now always return Invoke/Common receipts (no implicit deploy receipt).
  • Tests

    • Updated integration tests and subscription flows to reflect new receipt variants and parsing.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Aug 22, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

  • Removed StarknetTransaction::get_deployed_address_from_events and the event-based deployed-address inference.
  • Refactored receipt construction to return:
    • TransactionReceipt::DeployAccount(DeployAccountTransactionReceipt { .. }) for DeployAccount.
    • TransactionReceipt::Common(common_receipt) for Invoke, including UDC-based deployments.
    • TransactionReceipt::L1Handler(L1HandlerTransactionReceipt { .. }) for L1 handlers.
  • Added DeployAccount variant and DeployAccountTransactionReceipt struct in starknet-devnet-types, and updated finality/block-property handling to include DeployAccount.common.
  • Updated tests to expect Invoke receipts for UDC deployments and to validate specific UDC/STRK events.
  • Adjusted subscription test to parse deployment notifications as InvokeTransactionReceipt.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Receipts of deployments via UDC should have type INVOKE instead of DEPLOY (#834)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Introduce DeployAccount variant and DeployAccountTransactionReceipt (crates/starknet-devnet-types/src/rpc/transaction_receipt.rs, multiple lines) Not required by the objective which only targets UDC deployment receipts being typed as INVOKE.
Add/route L1HandlerTransactionReceipt in core get_receipt flow (crates/starknet-devnet-core/src/transactions.rs, L1Handler branch) L1 handler receipt handling is unrelated to UDC deployment receipt typing.
Update finality/block-property logic to include DeployAccount.common (crates/starknet-devnet-types/src/rpc/transaction_receipt.rs, methods affecting finality_status and block fields) Finality logic for DeployAccount is outside the scope of changing UDC deployments to INVOKE.

Possibly related PRs

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-deploy-receipt

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@FabijanC FabijanC marked this pull request as ready for review August 25, 2025 08:56
@FabijanC
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Aug 25, 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
Copy Markdown

@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: 0

🧹 Nitpick comments (6)
crates/starknet-devnet-types/src/rpc/transaction_receipt.rs (3)

24-30: Harden deserialization: deny unknown fields on DeployAccountTransactionReceipt.

For parity with CommonTransactionReceipt and to catch future wire-shape drift during tests, add serde(deny_unknown_fields) (gated by testing) here as well.

-#[derive(Debug, Clone, Serialize)]
-#[cfg_attr(feature = "testing", derive(serde::Deserialize))]
+#[derive(Debug, Clone, Serialize)]
+#[cfg_attr(feature = "testing", derive(serde::Deserialize), serde(deny_unknown_fields))]
 pub struct DeployAccountTransactionReceipt {

14-22: Optional: consider making the enum deserialization explicit in tests.

If you keep #[serde(untagged)], you can still avoid ambiguity in tests by providing a custom Deserialize (only under testing) that dispatches by the flattened "type" field:

// This snippet is additional code (not a diff) and should replace the derived Deserialize.
// Guard under `#[cfg(feature = "testing")]`.

#[cfg(feature = "testing")]
impl<'de> serde::Deserialize<'de> for TransactionReceipt {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        use serde::de::Error as _;
        let v = serde_json::Value::deserialize(deserializer)?;
        let ty = v.get("type").and_then(|t| t.as_str())
            .ok_or_else(|| D::Error::custom("missing `type` field on receipt"))?;
        match ty {
            "DEPLOY_ACCOUNT" => {
                Ok(TransactionReceipt::DeployAccount(
                    serde_json::from_value(v).map_err(D::Error::custom)?
                ))
            }
            "DEPLOY" => {
                Ok(TransactionReceipt::Deploy(
                    serde_json::from_value(v).map_err(D::Error::custom)?
                ))
            }
            "INVOKE" => {
                Ok(TransactionReceipt::Common(
                    serde_json::from_value(v).map_err(D::Error::custom)?
                ))
            }
            "L1_HANDLER" => {
                Ok(TransactionReceipt::L1Handler(
                    serde_json::from_value(v).map_err(D::Error::custom)?
                ))
            }
            _ => Err(D::Error::custom(format!("unknown receipt type `{ty}`"))),
        }
    }
}

Also applies to: 73-89


18-22: Receipt update is correct; untagged-deserialize risk is low but consider a safer impl

A quick search found no direct calls to serde_json::from_*::<TransactionReceipt> in the repo, so this enum isn’t currently deserialized anywhere (tests/benches/examples won’t hit the ambiguity). Nevertheless, because both DeployAccountTransactionReceipt and DeployTransactionReceipt flatten CommonTransactionReceipt (including contract_address), the #[serde(untagged)] enum remains order-dependent and could mis-deserialize if used in the future.

• Location:

  • crates/starknet-devnet-types/src/rpc/transaction_receipt.rs (the TransactionReceipt enum with #[serde(untagged)])

• Optional refactor: under the testing feature, drop the derived Deserialize and add a custom impl keyed on the "type" field, e.g.:

// Keep Serialize (for emitting), but only derive Deserialize when needed.
#[derive(Serialize)]
#[cfg_attr(feature = "testing", derive(Deserialize))]
#[serde(untagged)]
pub enum TransactionReceipt { /* … */ }

// Alternatively, implement Deserialize manually under `testing`:
#[cfg(feature = "testing")]
impl<'de> Deserialize<'de> for TransactionReceipt {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let val = serde_json::Value::deserialize(deserializer)?;
        let ty = val.get("type").and_then(|v| v.as_str())
            .ok_or_else(|| D::Error::custom("missing `type` field in receipt"))?;
        match ty {
            "DEPLOY_ACCOUNT" => DeployAccountTransactionReceipt::deserialize(val)
                .map(TransactionReceipt::DeployAccount)
                .map_err(D::Error::custom),
            "DEPLOY" => DeployTransactionReceipt::deserialize(val)
                .map(TransactionReceipt::Deploy)
                .map_err(D::Error::custom),
            "L1_HANDLER" => L1HandlerTransactionReceipt::deserialize(val)
                .map(TransactionReceipt::L1Handler)
                .map_err(D::Error::custom),
            "COMMON" => CommonTransactionReceipt::deserialize(val)
                .map(TransactionReceipt::Common)
                .map_err(D::Error::custom),
            other => Err(D::Error::custom(format!("unknown receipt type `{}`", other))),
        }
    }
}

Feel free to drop the Deserialize derive entirely if it’s only ever used in tests.

tests/integration/test_subscription_to_new_tx_receipts.rs (1)

95-105: Minor: avoid moving sockets unnecessarily in the loop.

The pattern for (mut ws, subscription_id) in [(explicit_ws, ...), (implicit_ws, ...)] moves both sockets, which is fine here but slightly obscures ownership. Two explicit blocks (or a small helper) can be clearer when extending this test.

tests/integration/get_transaction_receipt_by_hash.rs (2)

98-122: Event assertions: prefer matching by identity over positional indices for robustness.

The test currently asserts events[0] is the UDC ContractDeployed and events[1] is the STRK Transfer. While this is true today, future fee-model or event-order changes could reorder events and produce brittle failures.

Suggest locating events by (from_address, key) pairs and then asserting their payloads.

-            assert_eq!(receipt.events.len(), 2);
-            let deployment_event = &receipt.events[0];
-            assert_eq!(deployment_event.from_address, UDC_LEGACY_CONTRACT_ADDRESS);
-            assert_eq!(
-                deployment_event.keys,
-                vec![get_selector_from_name("ContractDeployed").unwrap()]
-            );
-            assert_eq!(deployment_event.data[0], expected_contract_address);
-            assert_eq!(deployment_event.data[1], account_address);
-
-            let fee_charge_event = &receipt.events[1];
-            assert_eq!(fee_charge_event.from_address, STRK_ERC20_CONTRACT_ADDRESS);
-            assert_eq!(fee_charge_event.keys, vec![get_selector_from_name("Transfer").unwrap()]);
-            assert_eq!(fee_charge_event.data[0], account_address);
+            // UDC ContractDeployed
+            let udc_key = get_selector_from_name("ContractDeployed").unwrap();
+            let deployment_event = receipt.events.iter().find(|e|
+                e.from_address == UDC_LEGACY_CONTRACT_ADDRESS && e.keys == vec![udc_key]
+            ).expect("missing UDC ContractDeployed event");
+            assert_eq!(deployment_event.data[0], expected_contract_address);
+            assert_eq!(deployment_event.data[1], account_address);
+
+            // STRK fee transfer
+            let transfer_key = get_selector_from_name("Transfer").unwrap();
+            let fee_charge_event = receipt.events.iter().find(|e|
+                e.from_address == STRK_ERC20_CONTRACT_ADDRESS && e.keys == vec![transfer_key]
+            ).expect("missing STRK Transfer event");
+            assert_eq!(fee_charge_event.data[0], account_address);

219-221: Make gas-underfunding deterministic without hardcoding.

Use the estimated l2_gas_consumed - 1 rather than a magic number to reduce flakiness across versions/backends.

-        // .l2_gas(u64::try_from(fee.l2_gas_consumed).unwrap() - 1) // subtract to induce failure
-        .l2_gas(1156800 - 1) // TODO: determined experimentally as the actual value used minus 1
+        .l2_gas(u64::try_from(fee.l2_gas_consumed).unwrap() - 1) // subtract to induce failure
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9601cc2 and b28c55c.

📒 Files selected for processing (4)
  • crates/starknet-devnet-core/src/transactions.rs (3 hunks)
  • crates/starknet-devnet-types/src/rpc/transaction_receipt.rs (3 hunks)
  • tests/integration/get_transaction_receipt_by_hash.rs (3 hunks)
  • tests/integration/test_subscription_to_new_tx_receipts.rs (2 hunks)
🔇 Additional comments (10)
crates/starknet-devnet-types/src/rpc/transaction_receipt.rs (2)

48-58: Coverage for finality and block-property clearing looks complete.

DeployAccount is correctly wired into both finality_status() and clear_block_properties(). No functional concerns.

Also applies to: 60-70


91-107: ExecutionResources mapping: OK.

Fields map cleanly from blockifier; types are u64 which matches usage in tests (comparisons/upper-bounds).

tests/integration/test_subscription_to_new_tx_receipts.rs (2)

183-184: Parsing UDC deployment notifications as Invoke is correct.

Deserializing the filtered deployment notification into InvokeTransactionReceipt is consistent with the new receipt semantics.


7-8: No outstanding DeployTransactionReceipt references in tests or subscription code

A search for DeployTransactionReceipt only returned its type definition in crates/starknet-devnet-types/src/rpc/transaction_receipt.rs, which is expected for RPC support. There are no imports or uses of it in your subscription tests or related code that could cause mismatches.

• Found definitions (expected):
– crates/starknet-devnet-types/src/rpc/transaction_receipt.rs:19 Deploy(DeployTransactionReceipt),
– crates/starknet-devnet-types/src/rpc/transaction_receipt.rs:34 pub struct DeployTransactionReceipt { … }
• Tests in tests/integration/test_subscription_to_new_tx_receipts.rs correctly import and use only InvokeTransactionReceipt.

tests/integration/get_transaction_receipt_by_hash.rs (1)

63-70: Variable naming/readability improvement is good.

Renaming to account_address clarifies intent across the test.

crates/starknet-devnet-core/src/transactions.rs (5)

155-165: Common receipt construction is clean and immutable now.

Switching to an immutable common_receipt improves clarity. The parameters cover events, messages, block props, execution result, finality, fee units, and resources: OK.


173-180: L1 handler message hash inclusion: good addition; error propagation OK.

MessageToL2::try_from(l1_transaction)?.hash()? captures the message hash and cleanly bubbles up errors via DevnetResult.


147-154: Fee units mapping aligns with tx versions.

  • L1Handler → WEI
  • V3 (Declare/DeployAccount/Invoke) → FRI
  • Else → WEI

This matches expected tokenomics; no concerns.


95-135: Event and message extraction: stable ordering ensured.

Recursive traversal with explicit sort by emitted order before extending preserves a deterministic sequence. This supports event-order-dependent tests.

Also applies to: 201-239


166-174: Review Approved: UDC invoke receipts correctly set

All branches now produce the intended receipt types:

  • In crates/starknet-devnet-core/src/transactions.rs, the Transaction::DeployAccount arm returns a DeployAccountTransactionReceipt with the correct contract_address, and the Transaction::Invoke arm returns TransactionReceipt::Common(common_receipt).
  • In crates/starknet-devnet-types/src/rpc/transactions.rs, create_common_receipt calls self.get_type(), and get_type maps Transaction::Invoke(_) to TransactionType::Invoke.

This confirms that UDC deployments now emit INVOKE receipts as intended (per issue #834). No further changes needed.

@FabijanC FabijanC merged commit f0df966 into main Aug 25, 2025
1 of 2 checks passed
@FabijanC FabijanC deleted the fix-deploy-receipt branch August 25, 2025 09:25
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.

Receipts of deployments via UDC should have type INVOKE instead of DEPLOY

1 participant