Skip to content

refactor: unify AuctionContract Result APIs and structured events#414

Merged
ryzen-xp merged 6 commits into
Alien-Protocol:devfrom
Amarjeet325:refactor/unify-auctioncontract-result-apis-and-structured-events
Apr 24, 2026
Merged

refactor: unify AuctionContract Result APIs and structured events#414
ryzen-xp merged 6 commits into
Alien-Protocol:devfrom
Amarjeet325:refactor/unify-auctioncontract-result-apis-and-structured-events

Conversation

@Amarjeet325
Copy link
Copy Markdown
Contributor

@Amarjeet325 Amarjeet325 commented Apr 23, 2026

Closes #386

Summary

Refactored the AuctionContract to unify error handling and standardize structured event emission across all auction flows.

Changes Made

  • Removed all panic-based control flow from public entrypoints and helper functions
  • Converted fallible APIs (including indexed flows and refund logic) to return Result<_, AuctionError>
  • Replaced panic-driven guards with explicit error propagation
  • Standardized event emission for:
    • create
    • bid
    • close
    • claim
    • refund
  • Introduced a single structured event format for consistency

Internal Improvements

  • Updated shared helpers to propagate errors instead of panicking
  • Preserved existing storage and auction behavior (no functional changes)
  • Improved code consistency and maintainability

Tests

  • Updated existing tests for new Result-based APIs
  • Added coverage for:
    • success scenarios
    • error handling paths
    • structured event validation
Screenshot 2026-04-23 234230

Validation

  • cargo test passes
  • cargo clippy passes with no warnings
  • CI (Smart Contracts CI) passing

Summary by CodeRabbit

  • Refactor

    • Migrated auction contract from panic-based to structured error handling with explicit result types for all operations.
    • Redesigned event emission system to use typed contract events instead of manual publishing.
    • Enhanced storage operations to gracefully handle missing data with proper error propagation.
  • Tests

    • Updated event verification tests to validate newly structured typed events.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

Warning

Rate limit exceeded

@Amarjeet325 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 30 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 12 minutes and 30 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ea136423-175c-47e1-9a81-91bf919ff120

📥 Commits

Reviewing files that changed from the base of the PR and between ef7ff01 and 614efdc.

📒 Files selected for processing (6)
  • onchain/contracts/auction_contract/src/events.rs
  • onchain/contracts/auction_contract/src/indexed.rs
  • onchain/contracts/auction_contract/src/lib.rs
  • onchain/contracts/auction_contract/src/singleton.rs
  • onchain/contracts/auction_contract/src/storage.rs
  • onchain/contracts/auction_contract/src/test.rs
📝 Walkthrough

Walkthrough

The PR refactors the AuctionContract to unify error handling by replacing panic-based failures with explicit Result<_, AuctionError> returns across all entrypoints and helpers, while standardizing event emission using structured contract events derived from #[contractevent] attributes instead of manual topic/data tuples.

Changes

Cohort / File(s) Summary
Event Emission Refactor
onchain/contracts/auction_contract/src/events.rs
Replaces manual event publishing with typed #[contractevent]-derived event structs (BidPlacedEvent, BidRefundedEvent). Event payload fields now sourced from struct properties instead of explicit topic/data tuples with BID_PLCD/BID_RFDN symbols.
Result-Based API Refactoring
onchain/contracts/auction_contract/src/indexed.rs, onchain/contracts/auction_contract/src/lib.rs, onchain/contracts/auction_contract/src/storage.rs
Public functions (create_auction, place_bid, close_auction_by_id, claim, refund_bid) now return Result<_, AuctionError> instead of () or panicking. Storage helpers (auction_get_seller, auction_get_asset) now return Result instead of panicking on missing entries. Error propagation via ? operator replaces panic_with_error! calls.
Helper Function Updates
onchain/contracts/auction_contract/src/singleton.rs
Updated require_status invocations in close_auction and claim_username to use )?; for error propagation and remove unused env argument.
Test Updates
onchain/contracts/auction_contract/src/test.rs
Event validation logic changed to detect specific events by symbol name (bid_refunded_event, bid_placed_event) instead of decoding and validating payload contents, aligning with structured event approach.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #269 — Related changes to require_status invocation pattern in singleton.rs (switching to Result-returning helper with ? operator).

Suggested labels

Approved

Suggested reviewers

  • ryzen-xp

Poem

🐰 Hops with glee through refactored code,
No panics now on errors' road!
Structured events in bright array,
Results all the way, hip-hooray!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.00% 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 describes the main refactoring work: unifying Result-based APIs and standardizing structured event emission across the AuctionContract.
Linked Issues check ✅ Passed The pull request implementation comprehensively addresses all requirements from #386: Result APIs unified across all fallible functions, panic-based control flow removed from entrypoints, structured events standardized, tests updated, and cargo test/clippy pass.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the refactoring scope defined in #386; no unrelated modifications to storage semantics, auction behavior, or out-of-scope features are present.

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

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

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.

@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Apr 23, 2026

@Amarjeet325 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

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: 2

🧹 Nitpick comments (4)
onchain/contracts/auction_contract/src/events.rs (1)

4-8: Old symbol_short! constants are now dead code.

With #[contractevent] structs publishing events via .publish(env) (which uses snake_case of the struct name as the event symbol, e.g. bid_placed_event), these manual Symbol constants are no longer referenced by any emission path. They're only kept alive by _touch_event_symbols in lib.rs:20-28. Consider removing both the constants here and the _touch_event_symbols helper + imports in lib.rs as part of the cleanup.

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

In `@onchain/contracts/auction_contract/src/events.rs` around lines 4 - 8, Remove
the unused manual Symbol constants (AUCTION_CREATED, BID_PLACED, AUCTION_CLOSED,
USERNAME_CLAIMED, BID_REFUNDED) from events.rs and delete the related dead-code
touch helper and its imports in lib.rs (the `_touch_event_symbols` function and
any imports solely used to keep those constants alive); ensure no other code
references these constants before removing them and run tests to verify contract
event emission now relies on the #[contractevent] structs' snake_case symbols.
onchain/contracts/auction_contract/src/lib.rs (2)

102-104: Semantic misuse: AlreadyClaimed returned for a double-refund case.

AlreadyClaimed (1002) refers to username claim semantics; using it here conflates two distinct domain concepts and makes off-chain error handling ambiguous (a client can't tell whether a refund or a claim was duplicated). Consider adding a dedicated AlreadyRefunded variant in errors.rs and returning it here; update test_refund_bid_double_refund_panics accordingly.

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

In `@onchain/contracts/auction_contract/src/lib.rs` around lines 102 - 104, The
code currently returns errors::AuctionError::AlreadyClaimed from the refund
guard when storage::auction_is_bid_refunded(&env, id, &bidder) is true, which is
the wrong semantic; add a new enum variant AuctionError::AlreadyRefunded in
errors.rs, update any matching arms/exports, change the return here to
Err(errors::AuctionError::AlreadyRefunded), and adjust the test
test_refund_bid_double_refund_panics to expect AlreadyRefunded instead of
AlreadyClaimed.

14-28: _touch_event_symbols and the old symbol imports can be removed.

Since events now use #[contractevent]-derived structs which auto-generate their own event symbols on .publish(env), the manual symbol_short! constants in events.rs and the _touch_event_symbols shim keeping them alive against WASM linker stripping are no longer needed. Dropping them simplifies the module and removes dead symbols from the compiled artifact. See the related comment on events.rs:4-8.

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

In `@onchain/contracts/auction_contract/src/lib.rs` around lines 14 - 28, Remove
the dead shim and old imports: delete the `_touch_event_symbols` function and
its tuple reference to AUCTION_CREATED, BID_PLACED, AUCTION_CLOSED,
USERNAME_CLAIMED, BID_REFUNDED from lib.rs, and also remove any now-unused `use
crate::events::{...}` lines that only served that shim; ensure no other code
references `_touch_event_symbols` and run a build to confirm the redundant
symbol constants and imports can be dropped safely.
onchain/contracts/auction_contract/src/indexed.rs (1)

16-24: Error variants are overloaded across semantically distinct conditions.

Several distinct failure modes collapse to the same variant, which makes off-chain error handling ambiguous:

  • create_auction returns AuctionNotOpen for "auction already exists" (Line 17) and AuctionNotClosed for "end_time in the past" (Line 20).
  • place_bid returns AuctionNotOpen for "auction missing" (Line 41), "status not Open" (Line 44), and "past end_time" (Line 48) — three different causes under one code.

Consider introducing more precise variants (e.g., AuctionAlreadyExists, InvalidEndTime, AuctionExpired) so clients can distinguish the actual failure. This was an acceptance criterion of the refactor ("consistent structured errors"), and the tests in test.rs currently only pin error codes, not cause.

Also applies to: 40-49

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

In `@onchain/contracts/auction_contract/src/indexed.rs` around lines 16 - 24, The
current errors::AuctionError enum overloads variants causing ambiguity; add
distinct variants like AuctionAlreadyExists, InvalidEndTime (or
AuctionEndInPast), AuctionMissing, AuctionNotOpenStatus, and AuctionExpired,
then update create_auction to return AuctionAlreadyExists when
storage::auction_exists(env, id) is true and InvalidEndTime when end_time <=
env.ledger().timestamp(), and update place_bid to return AuctionMissing when
storage::auction_exists(env, id) is false, AuctionNotOpenStatus when the stored
status != Open, and AuctionExpired when end_time <= env.ledger().timestamp();
also update any match arms, tests (test.rs) and places that construct/compare
AuctionError to use the new variants.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@onchain/contracts/auction_contract/src/indexed.rs`:
- Around line 79-95: The close_auction_by_id function currently lacks a guard on
the auction status allowing duplicate closes; update close_auction_by_id to
fetch the current status via storage::auction_get_status(env, id) and verify it
equals types::AuctionStatus::Open before proceeding, returning
errors::AuctionError::AuctionNotOpen (or another appropriate error) if not Open,
then only call storage::auction_set_status(env, id,
types::AuctionStatus::Closed) and emit events after that check; keep existing
timestamp and existence checks but insert the status guard immediately after
existence validation.

In `@onchain/contracts/auction_contract/src/lib.rs`:
- Line 122: The refund event is using a hardcoded zero username_hash which
diverges from other emitters; replace the literal &[0u8; 32] passed to
events::emit_bid_refunded in lib.rs with the auction's stored username hash by
calling storage::auction_get_username_hash(env, id) (the same lookup used in
indexed.rs) and pass that value into emit_bid_refunded so the refund event uses
the actual username_hash for that auction ID.

---

Nitpick comments:
In `@onchain/contracts/auction_contract/src/events.rs`:
- Around line 4-8: Remove the unused manual Symbol constants (AUCTION_CREATED,
BID_PLACED, AUCTION_CLOSED, USERNAME_CLAIMED, BID_REFUNDED) from events.rs and
delete the related dead-code touch helper and its imports in lib.rs (the
`_touch_event_symbols` function and any imports solely used to keep those
constants alive); ensure no other code references these constants before
removing them and run tests to verify contract event emission now relies on the
#[contractevent] structs' snake_case symbols.

In `@onchain/contracts/auction_contract/src/indexed.rs`:
- Around line 16-24: The current errors::AuctionError enum overloads variants
causing ambiguity; add distinct variants like AuctionAlreadyExists,
InvalidEndTime (or AuctionEndInPast), AuctionMissing, AuctionNotOpenStatus, and
AuctionExpired, then update create_auction to return AuctionAlreadyExists when
storage::auction_exists(env, id) is true and InvalidEndTime when end_time <=
env.ledger().timestamp(), and update place_bid to return AuctionMissing when
storage::auction_exists(env, id) is false, AuctionNotOpenStatus when the stored
status != Open, and AuctionExpired when end_time <= env.ledger().timestamp();
also update any match arms, tests (test.rs) and places that construct/compare
AuctionError to use the new variants.

In `@onchain/contracts/auction_contract/src/lib.rs`:
- Around line 102-104: The code currently returns
errors::AuctionError::AlreadyClaimed from the refund guard when
storage::auction_is_bid_refunded(&env, id, &bidder) is true, which is the wrong
semantic; add a new enum variant AuctionError::AlreadyRefunded in errors.rs,
update any matching arms/exports, change the return here to
Err(errors::AuctionError::AlreadyRefunded), and adjust the test
test_refund_bid_double_refund_panics to expect AlreadyRefunded instead of
AlreadyClaimed.
- Around line 14-28: Remove the dead shim and old imports: delete the
`_touch_event_symbols` function and its tuple reference to AUCTION_CREATED,
BID_PLACED, AUCTION_CLOSED, USERNAME_CLAIMED, BID_REFUNDED from lib.rs, and also
remove any now-unused `use crate::events::{...}` lines that only served that
shim; ensure no other code references `_touch_event_symbols` and run a build to
confirm the redundant symbol constants and imports can be dropped safely.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2063cb05-30e3-483e-937d-6d717ebdd876

📥 Commits

Reviewing files that changed from the base of the PR and between d5b2c62 and ef7ff01.

📒 Files selected for processing (6)
  • onchain/contracts/auction_contract/src/events.rs
  • onchain/contracts/auction_contract/src/indexed.rs
  • onchain/contracts/auction_contract/src/lib.rs
  • onchain/contracts/auction_contract/src/singleton.rs
  • onchain/contracts/auction_contract/src/storage.rs
  • onchain/contracts/auction_contract/src/test.rs

Comment on lines +79 to 95
pub fn close_auction_by_id(env: &Env, id: u32) -> Result<(), errors::AuctionError> {
if !storage::auction_exists(env, id) {
soroban_sdk::panic_with_error!(env, errors::AuctionError::AuctionNotOpen);
return Err(errors::AuctionError::AuctionNotOpen);
}
let end_time = storage::auction_get_end_time(env, id);
if env.ledger().timestamp() < end_time {
soroban_sdk::panic_with_error!(env, errors::AuctionError::AuctionNotClosed);
return Err(errors::AuctionError::AuctionNotClosed);
}
storage::auction_set_status(env, id, types::AuctionStatus::Closed);

let username_hash = storage::auction_get_username_hash(env, id);
let winner = storage::auction_get_highest_bidder(env, id);
let winning_bid = storage::auction_get_highest_bid(env, id) as u128;
events::emit_auction_closed(env, &username_hash, winner, winning_bid);

Ok(())
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing status guard in close_auction_by_id allows duplicate close events.

There's no check that the auction is currently Open. After the auction has been closed (or even claimed — auction_set_claimed is a separate flag and does not mutate Status), any caller can re-invoke close_auction_by_id since:

  • auction_exists still returns true
  • env.ledger().timestamp() >= end_time still holds
  • No auth is required on this entrypoint (see lib.rs:127)

This re-emits auction_closed_event with the stored (possibly stale) highest bidder/bid, which will confuse off-chain indexers and event consumers. Add an explicit AuctionStatus::Open guard up front.

🛡️ Proposed fix
 pub fn close_auction_by_id(env: &Env, id: u32) -> Result<(), errors::AuctionError> {
     if !storage::auction_exists(env, id) {
         return Err(errors::AuctionError::AuctionNotOpen);
     }
+    if storage::auction_get_status(env, id) != types::AuctionStatus::Open {
+        return Err(errors::AuctionError::AuctionNotOpen);
+    }
     let end_time = storage::auction_get_end_time(env, id);
     if env.ledger().timestamp() < end_time {
         return Err(errors::AuctionError::AuctionNotClosed);
     }
     storage::auction_set_status(env, id, types::AuctionStatus::Closed);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@onchain/contracts/auction_contract/src/indexed.rs` around lines 79 - 95, The
close_auction_by_id function currently lacks a guard on the auction status
allowing duplicate closes; update close_auction_by_id to fetch the current
status via storage::auction_get_status(env, id) and verify it equals
types::AuctionStatus::Open before proceeding, returning
errors::AuctionError::AuctionNotOpen (or another appropriate error) if not Open,
then only call storage::auction_set_status(env, id,
types::AuctionStatus::Closed) and emit events after that check; keep existing
timestamp and existence checks but insert the status guard immediately after
existence validation.

Comment thread onchain/contracts/auction_contract/src/lib.rs Outdated
@ryzen-xp ryzen-xp self-requested a review April 24, 2026 01:24
@ryzen-xp ryzen-xp added the APPROVED This PR is ready for merging . label Apr 24, 2026
@ryzen-xp ryzen-xp merged commit b4b3144 into Alien-Protocol:dev Apr 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

APPROVED This PR is ready for merging .

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: unify AuctionContract Result APIs and structured events

3 participants