Skip to content

refactor: finish EscrowContract structured errors and events#448

Merged
ryzen-xp merged 2 commits into
Alien-Protocol:devfrom
Yash-Karakoti:refactor/finish-escrowcontract-structured-errors-and-events
May 3, 2026
Merged

refactor: finish EscrowContract structured errors and events#448
ryzen-xp merged 2 commits into
Alien-Protocol:devfrom
Yash-Karakoti:refactor/finish-escrowcontract-structured-errors-and-events

Conversation

@Yash-Karakoti
Copy link
Copy Markdown
Contributor

@Yash-Karakoti Yash-Karakoti commented Apr 26, 2026

🚀 Alien Protocol Pull Request


📌 Type of Change

  • Documentation (updates to README, docs, or comments)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

📝 Changes description

What changed and why:
Refactored the EscrowContract to use structured errors and events instead of panic-based control flows. This improves contract safety, predictability, and aligns with the project's Result-based API standards.

Context & Trade-offs:

  • Replaced panic_with_error! macros with explicit Result<_, EscrowError> returns across all public APIs (initialize, create_vault, deposit, withdraw, trigger_auto_pay, etc.).
  • Added an ArithmeticError variant to EscrowError to handle math operations safely via checked_add and checked_sub instead of using .expect().
  • Upgraded the vault_crt event to use the #[contractevent] structured pattern.
  • Updated all E2E and unit tests in test.rs to expect standard explicit errors rather than trapped panics.

Steps to test or verify the change:

  1. Navigate to the onchain/contracts/escrow_contract directory.
  2. Run cargo clippy --all-targets --all-features -- -D warnings to verify no linter warnings.
  3. Run cargo test to verify all success, failure, and error-handling tests pass.

📸 Evidence

Evidence video recording for tests


⏰ Time spent breakdown

4 hours (Refactoring panic flows, updating math operations, standardizing events, and rewriting test assertions).


🌌 Comments

All Smart Contract CI checks pass cleanly locally.

Summary by CodeRabbit

Release Notes

  • Refactor

    • Contract methods now return explicit errors instead of panicking, providing clearer error handling and reporting.
    • Event system upgraded to strongly-typed event structures for improved reliability and type safety.
    • Arithmetic operations now safely handle overflow and underflow conditions.
  • Tests

    • Updated test suite to verify error handling and contract failure scenarios.

@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Apr 26, 2026

@Yash-Karakoti Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 26, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8c2ec725-0edc-4c68-a4f0-c215a58e0076

📥 Commits

Reviewing files that changed from the base of the PR and between e771e13 and 1260f88.

📒 Files selected for processing (3)
  • onchain/contracts/escrow_contract/src/events.rs
  • onchain/contracts/escrow_contract/src/lib.rs
  • onchain/contracts/escrow_contract/src/test.rs

📝 Walkthrough

Walkthrough

The EscrowContract refactors from panic-based error handling to explicit Result<_, EscrowError> APIs across five core methods. Event emission migrates from tuple-based symbol_short!("VAULT_CRT") to a strongly-typed VaultCrtEvent struct. Arithmetic operations now use checked_add/checked_sub with error mapping instead of unchecked variants.

Changes

EscrowContract Structured Errors and Events

Layer / File(s) Summary
Event Definition
onchain/contracts/escrow_contract/src/events.rs
New #[contractevent] struct VaultCrtEvent with commitment as topic, token, and owner fields. vault_crt method now publishes VaultCrtEvent instance; deprecated symbol_short! publication removed.
Fallible Method Signatures
onchain/contracts/escrow_contract/src/lib.rs
initialize, create_vault, withdraw, cancel_auto_pay, trigger_auto_pay all change to return Result<(), EscrowError> instead of panicking. resolve helper becomes fallible, returning Result<Address, EscrowError>.
Error Returns & Propagation
onchain/contracts/escrow_contract/src/lib.rs
Replace panic_with_error! and expect() with explicit Err(EscrowError::*) returns and ? propagation. Coverage includes already-initialized, vault-not-found, vault-already-exists, inactive-vault, insufficient-balance, invalid-amount, self-payment, and auto-pay-not-found errors.
Arithmetic Overflow Handling
onchain/contracts/escrow_contract/src/lib.rs
Replace unchecked + and - with checked_add(...).ok_or(EscrowError::ArithmeticError)? and checked_sub(...).ok_or(EscrowError::ArithmeticError)? in deposit, withdraw, and trigger_auto_pay.
Tests & Integration
onchain/contracts/escrow_contract/src/test.rs, onchain/tests/e2e/test_full_flow.rs
Assertion pattern changes from matches!(..., Error::from_contract_error(...)) to direct equality Err(Ok(EscrowError::*)). E2E test unwraps create_vault results. Invalid-amount test updated to call try_withdraw for both 0 and negative amounts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

Suggested labels

Approved

Suggested reviewers

  • ryzen-xp

🐰 Panics are now past,
Results flow graceful and fast,
Typed events at last,
Errors explicit, cast—
Solid contracts will last!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main refactoring change: transitioning EscrowContract from panic-based error handling to structured errors and events.
Linked Issues check ✅ Passed The pull request successfully addresses all primary coding requirements from issue #387: Result-based APIs, ArithmeticError handling, structured events, and test updates.
Out of Scope Changes check ✅ Passed All changes are directly related to issue #387's objectives; no out-of-scope modifications were detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value).


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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

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.

Caution

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

⚠️ Outside diff range comments (1)
onchain/contracts/escrow_contract/src/test.rs (1)

641-654: ⚠️ Potential issue | 🟡 Minor

Test no longer exercises deposit despite its name.

test_deposit_invalid_amount now calls try_withdraw for both the 0 and -50 cases (the // or try_deposit comments confirm the substitution). This is a coverage regression: the deposit path's amount <= 0 guard is no longer asserted here, and it duplicates test_withdraw_invalid_amount. test_deposit_zero_panics covers the zero case but the negative-amount case for deposit is now untested.

🔧 Proposed fix: restore `try_deposit` coverage
-    let result0 = client.try_withdraw(&from, &0); // or try_deposit
-    assert_eq!(result0, Err(Ok(EscrowError::InvalidAmount)));
-
-    let result_neg = client.try_withdraw(&from, &-50); // or try_deposit
-    assert_eq!(result_neg, Err(Ok(EscrowError::InvalidAmount)));
+    let result0 = client.try_deposit(&from, &0);
+    assert_eq!(result0, Err(Ok(EscrowError::InvalidAmount)));
+
+    let result_neg = client.try_deposit(&from, &-50);
+    assert_eq!(result_neg, Err(Ok(EscrowError::InvalidAmount)));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@onchain/contracts/escrow_contract/src/test.rs` around lines 641 - 654, The
test named test_deposit_invalid_amount currently calls client.try_withdraw
instead of exercising the deposit path; change both occurrences of
client.try_withdraw(&from, &0) and client.try_withdraw(&from, &-50) back to
client.try_deposit(&from, &0) and client.try_deposit(&from, &-50) respectively
so the deposit guard (deposit/try_deposit) is asserted and returns
Err(Ok(EscrowError::InvalidAmount)) for both zero and negative amounts; keep the
rest of the setup (setup_test, create_vault, EscrowError::InvalidAmount,
owner/from variables) unchanged.
🧹 Nitpick comments (4)
onchain/contracts/escrow_contract/src/test.rs (1)

271-272: Misleading _panics suffixes after the Result-based migration.

Several tests still carry _panics in their name but now assert Err(Ok(EscrowError::...)) rather than panics (e.g., test_schedule_payment_past_release_panics, test_schedule_payment_insufficient_balance_panics, test_execute_scheduled_early_panics, test_execute_scheduled_double_panics, test_execute_scheduled_not_found_panics, test_deposit_zero_panics). Consider renaming to *_returns_error (or similar) for accuracy. The remaining #[should_panic] tests (auth failures, non-owner) can keep the _panics suffix.

Also applies to: 291-292, 404-405, 529-530, 559-560, 783-784

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@onchain/contracts/escrow_contract/src/test.rs` around lines 271 - 272, Rename
the misleading test functions that still use the _panics suffix to reflect they
now return Err results; for example rename
test_schedule_payment_past_release_panics to
test_schedule_payment_past_release_returns_error and similarly update
test_schedule_payment_insufficient_balance_panics,
test_execute_scheduled_early_panics, test_execute_scheduled_double_panics,
test_execute_scheduled_not_found_panics, and test_deposit_zero_panics to
*_returns_error (or another consistent *_returns_err naming); update their
function identifiers and any references in the test module so the names match
the new behavior while leaving true #[should_panic] tests (auth/non-owner)
unchanged.
onchain/tests/e2e/test_full_flow.rs (1)

131-131: Optional: prefer .expect("...") over .unwrap() in test setup.

Using .expect("create_vault should succeed") (and similar for the recipient vault on line 166) provides a clearer failure message if the test ever regresses, without changing semantics.

Also applies to: 166-166

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@onchain/tests/e2e/test_full_flow.rs` at line 131, Replace panic-style
unwrap() calls in test setup with expect(...) messages to give clearer failure
context: change the EscrowContract::create_vault(...).unwrap() invocation to use
.expect("create_vault should succeed") and do the same for the recipient vault
creation call later (the other EscrowContract::create_vault(...) invocation)
using a descriptive message like .expect("create_recipient_vault should
succeed").
onchain/contracts/escrow_contract/src/lib.rs (2)

168-169: Inconsistent: raw -= here while other paths use checked_sub.

deposit, withdraw, and trigger_auto_pay were updated to use checked_add/checked_sub mapped to EscrowError::ArithmeticError, but schedule_payment still uses state.balance -= amount. The guard at line 164 (state.balance < amount) makes this safe today, but using checked_sub here would match the PR's stated goal of replacing arithmetic that can panic and would harden against future refactors that loosen the precondition.

♻️ Proposed fix
-        state.balance -= amount;
+        state.balance = state
+            .balance
+            .checked_sub(amount)
+            .ok_or(EscrowError::ArithmeticError)?;
         write_vault_state(&env, &from, &state);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@onchain/contracts/escrow_contract/src/lib.rs` around lines 168 - 169, The
subtraction in schedule_payment currently uses raw state.balance -= amount while
other paths use checked arithmetic; replace that in schedule_payment by using
state.balance.checked_sub(amount).ok_or(EscrowError::ArithmeticError)? and
propagate the error (matching deposit/withdraw/trigger_auto_pay behavior), then
call write_vault_state(&env, &from, &state) as before so the function
consistently uses checked_sub and returns EscrowError::ArithmeticError on
underflow.

388-391: Minor: consider a more accurate error for resolve on missing recipient vault.

resolve is called for payment.to in execute_scheduled and auto_pay.to in trigger_auto_pay. If the recipient hasn't set up a vault, the caller now sees EscrowError::VaultNotFound, which is ambiguous with the source-vault check earlier in those flows. A dedicated variant (e.g., RecipientVaultNotFound) — or at least a doc comment clarifying the meaning — would make the failure mode unambiguous for off-chain consumers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@onchain/contracts/escrow_contract/src/lib.rs` around lines 388 - 391, The
resolve function currently maps a missing recipient vault to
EscrowError::VaultNotFound which is ambiguous with source-vault errors in
execute_scheduled and trigger_auto_pay; add a distinct
EscrowError::RecipientVaultNotFound (or similarly named variant) to the
EscrowError enum and have resolve return that variant when
read_vault_config(env, commitment) is None, then update any error
propagation/handling and tests that expect VaultNotFound so off-chain callers
can unambiguously detect a missing recipient vault; update doc comments for
resolve/read_vault_config to note this behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@onchain/contracts/escrow_contract/src/test.rs`:
- Around line 641-654: The test named test_deposit_invalid_amount currently
calls client.try_withdraw instead of exercising the deposit path; change both
occurrences of client.try_withdraw(&from, &0) and client.try_withdraw(&from,
&-50) back to client.try_deposit(&from, &0) and client.try_deposit(&from, &-50)
respectively so the deposit guard (deposit/try_deposit) is asserted and returns
Err(Ok(EscrowError::InvalidAmount)) for both zero and negative amounts; keep the
rest of the setup (setup_test, create_vault, EscrowError::InvalidAmount,
owner/from variables) unchanged.

---

Nitpick comments:
In `@onchain/contracts/escrow_contract/src/lib.rs`:
- Around line 168-169: The subtraction in schedule_payment currently uses raw
state.balance -= amount while other paths use checked arithmetic; replace that
in schedule_payment by using
state.balance.checked_sub(amount).ok_or(EscrowError::ArithmeticError)? and
propagate the error (matching deposit/withdraw/trigger_auto_pay behavior), then
call write_vault_state(&env, &from, &state) as before so the function
consistently uses checked_sub and returns EscrowError::ArithmeticError on
underflow.
- Around line 388-391: The resolve function currently maps a missing recipient
vault to EscrowError::VaultNotFound which is ambiguous with source-vault errors
in execute_scheduled and trigger_auto_pay; add a distinct
EscrowError::RecipientVaultNotFound (or similarly named variant) to the
EscrowError enum and have resolve return that variant when
read_vault_config(env, commitment) is None, then update any error
propagation/handling and tests that expect VaultNotFound so off-chain callers
can unambiguously detect a missing recipient vault; update doc comments for
resolve/read_vault_config to note this behavior.

In `@onchain/contracts/escrow_contract/src/test.rs`:
- Around line 271-272: Rename the misleading test functions that still use the
_panics suffix to reflect they now return Err results; for example rename
test_schedule_payment_past_release_panics to
test_schedule_payment_past_release_returns_error and similarly update
test_schedule_payment_insufficient_balance_panics,
test_execute_scheduled_early_panics, test_execute_scheduled_double_panics,
test_execute_scheduled_not_found_panics, and test_deposit_zero_panics to
*_returns_error (or another consistent *_returns_err naming); update their
function identifiers and any references in the test module so the names match
the new behavior while leaving true #[should_panic] tests (auth/non-owner)
unchanged.

In `@onchain/tests/e2e/test_full_flow.rs`:
- Line 131: Replace panic-style unwrap() calls in test setup with expect(...)
messages to give clearer failure context: change the
EscrowContract::create_vault(...).unwrap() invocation to use
.expect("create_vault should succeed") and do the same for the recipient vault
creation call later (the other EscrowContract::create_vault(...) invocation)
using a descriptive message like .expect("create_recipient_vault should
succeed").

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 98913aab-917d-4d0b-8941-a68f566b7b2f

📥 Commits

Reviewing files that changed from the base of the PR and between 5e88bda and e771e13.

📒 Files selected for processing (5)
  • onchain/contracts/escrow_contract/src/events.rs
  • onchain/contracts/escrow_contract/src/lib.rs
  • onchain/contracts/escrow_contract/src/test.rs
  • onchain/shared/src/errors.rs
  • onchain/tests/e2e/test_full_flow.rs

@ryzen-xp ryzen-xp self-requested a review April 27, 2026 04:16
@ryzen-xp ryzen-xp added the CI Failed Please check why you CI is faileing fix your code label Apr 27, 2026
@ryzen-xp ryzen-xp merged commit 6208ddd into Alien-Protocol:dev May 3, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI Failed Please check why you CI is faileing fix your code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: finish EscrowContract structured errors and events

2 participants