Skip to content

[Soroban/Escrow] Add merchant cancellation before funding#167

Open
chiemezie1 wants to merge 1 commit into
DelegoLabs:mainfrom
chiemezie1:feature/93-merchant-cancellation
Open

[Soroban/Escrow] Add merchant cancellation before funding#167
chiemezie1 wants to merge 1 commit into
DelegoLabs:mainfrom
chiemezie1:feature/93-merchant-cancellation

Conversation

@chiemezie1

@chiemezie1 chiemezie1 commented Jun 25, 2026

Copy link
Copy Markdown

Add merchant cancellation before funding

PR Description

Summary

Adds merchant cancellation support for escrows that have been created but not yet funded in lib.rs.

What changed

  • Added EscrowStatus::Created and EscrowStatus::Cancelled
  • Added EscrowCancelledEvent
  • Added create_escrow(...) to create pending escrows
  • Added fund_escrow(...) to fund pending escrows
  • Added cancel_before_funding(...) to allow merchant cancellation before funds are locked
  • Added integration tests covering:
    • successful merchant cancellation before funding
    • failed cancellation after funding

Files changed

  • lib.rs
  • integration_tests.rs

Closes: #93

Summary by CodeRabbit

  • New Features

    • Added the ability to create escrows in a pending state, fund them later, or cancel them before funding.
    • Introduced clearer escrow status tracking, including pending and cancelled states.
    • Added a cancellation event so escrow updates are easier to track.
  • Bug Fixes

    • Prevents cancellation before funding once an escrow has already been funded.
    • Improves validation around escrow state transitions and access permissions.

@drips-wave

drips-wave Bot commented Jun 25, 2026

Copy link
Copy Markdown

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

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

EscrowStatus gains created and cancelled states, and the contract adds create, fund, and cancel-before-funding methods with a cancellation event. Integration tests cover pending escrow creation, merchant cancellation before funding, and rejection after funding.

Changes

Escrow lifecycle cancellation

Layer / File(s) Summary
State and event contract
contracts/escrow/src/lib.rs
EscrowStatus adds Created and Cancelled, and EscrowCancelledEvent adds escrow id, canceller, and reason fields.
Create, fund, and cancel methods
contracts/escrow/src/lib.rs
create_escrow stores a created record, fund_escrow advances it to funded, and cancel_before_funding marks it cancelled and emits the cancellation event.
Integration tests for cancellation
contracts/escrow/src/integration_tests.rs
The test suite adds a pending escrow helper, a successful merchant-cancel test, and a failure case after funding.

Sequence Diagram(s)

sequenceDiagram
  participant Seller
  participant Buyer
  participant Merchant
  participant EscrowContract

  Seller->>EscrowContract: create_escrow(...)
  EscrowContract-->>Seller: escrow_id, status Created

  Buyer->>EscrowContract: fund_escrow(escrow_id, buyer)
  EscrowContract-->>Buyer: status Funded

  Merchant->>EscrowContract: cancel_before_funding(escrow_id, merchant, reason)
  EscrowContract-->>Merchant: status Cancelled + EscrowCancelledEvent
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • DelegoLabs/Delego#41: Adds the EscrowError result flow used by the new failure assertion in test_cancel_after_funding_fails.
  • DelegoLabs/Delego#144: Updates the escrow state machine and lifecycle tests in the same contract area.
  • DelegoLabs/Delego#145: Shares the create_escrow path and whitelist validation used by the new pending escrow helper.

Suggested labels

size/S

Poem

A bunny hopped through escrow lore,
Where Created states appeared once more.
Then Cancelled chimed with tidy grace,
While tests left pawprints in their place.
🐰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: merchant cancellation before funding.
Linked Issues check ✅ Passed The PR implements merchant-authorized pre-funding cancellation, rejects post-funding cancellation, emits the requested event, and adds success/failure tests.
Out of Scope Changes check ✅ Passed The changes stay within the escrow contract and its integration tests, with no obvious unrelated additions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

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

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
contracts/escrow/src/lib.rs (4)

586-625: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor: auth/status check order differs from fund_escrow.

Here the seller-authorization check precedes the status check, whereas fund_escrow checks status before the caller match. The behavior is correct for both (and the post-funding failure test relies on the seller passing auth before hitting InvalidStatus), but aligning the ordering across lifecycle methods would aid readability. The byte-packing of escrow_id into the high-zero/low-8-bytes layout is correct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/escrow/src/lib.rs` around lines 586 - 625, The auth/status
validation order in cancel_before_funding is inconsistent with fund_escrow and
should be aligned for readability. Update cancel_before_funding in the Escrow
contract so the status check and merchant ownership/authorization flow matches
the lifecycle pattern used by fund_escrow, while preserving the current behavior
where a valid seller can reach InvalidStatus after auth. Keep the existing
escrow_id event packing and cancellation logic unchanged.

73-79: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

escrow_id typed as BytesN<32> diverges from other events using u64.

Every other escrow event (EscrowCreatedEvent, EscrowReleasedEvent, etc.) carries escrow_id: u64, while this one packs the id into BytesN<32>. This matches the shape requested in the linked issue, so no change is required, but it does force off-chain indexers to special-case decoding for this one event. Worth confirming the consumer is aware.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/escrow/src/lib.rs` around lines 73 - 79, Confirm the event schema
choice in EscrowCancelledEvent and, if the off-chain consumer expects
consistency, align escrow_id with the u64 shape used by EscrowCreatedEvent and
EscrowReleasedEvent; otherwise leave the BytesN<32> type as-is and ensure any
indexer/decoder handling EscrowCancelledEvent is updated to decode this one
event differently. Reference EscrowCancelledEvent and the other escrow event
structs in contracts::escrow::lib to keep the shape consistent with the intended
consumer contract.

487-543: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

create_escrow persists a pending record but emits no event.

Off-chain consumers cannot observe an escrow until fund_escrow later publishes escrow/created. A pending escrow created here and then cancel_before_funding-cancelled would surface only a cancelled event with no prior created, which can confuse indexers reconstructing the lifecycle. Consider emitting a dedicated pending/created event here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/escrow/src/lib.rs` around lines 487 - 543, The create_escrow flow
stores a new pending escrow in persistent storage but never emits an event, so
off-chain consumers cannot observe the escrow lifecycle start. Update
create_escrow in lib.rs to publish a dedicated escrow-created/pending event
immediately after saving the EscrowRecord, using the same event pattern as
fund_escrow and cancel_before_funding so indexers can reconstruct the full
lifecycle from create_escrow through later transitions.

525-525: 🩺 Stability & Availability | 🔵 Trivial

Avoid u32 overflow in timeout_ledger
Use checked_add or saturating_add for env.ledger().sequence() + timeout_ledgers here, and in deposit, to avoid wraparound when timeout_ledgers is large.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/escrow/src/lib.rs` at line 525, The `timeout_ledger` calculation
can overflow when adding `timeout_ledgers` to `env.ledger().sequence()`, so
replace the direct addition with a checked or saturating add in both this path
and the `deposit` flow. Update the logic around `timeout_ledger` so it handles
large `timeout_ledgers` safely, and use the relevant helpers in the escrow
contract’s timeout/deposit code paths to prevent wraparound.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@contracts/escrow/src/lib.rs`:
- Around line 586-625: The auth/status validation order in cancel_before_funding
is inconsistent with fund_escrow and should be aligned for readability. Update
cancel_before_funding in the Escrow contract so the status check and merchant
ownership/authorization flow matches the lifecycle pattern used by fund_escrow,
while preserving the current behavior where a valid seller can reach
InvalidStatus after auth. Keep the existing escrow_id event packing and
cancellation logic unchanged.
- Around line 73-79: Confirm the event schema choice in EscrowCancelledEvent
and, if the off-chain consumer expects consistency, align escrow_id with the u64
shape used by EscrowCreatedEvent and EscrowReleasedEvent; otherwise leave the
BytesN<32> type as-is and ensure any indexer/decoder handling
EscrowCancelledEvent is updated to decode this one event differently. Reference
EscrowCancelledEvent and the other escrow event structs in
contracts::escrow::lib to keep the shape consistent with the intended consumer
contract.
- Around line 487-543: The create_escrow flow stores a new pending escrow in
persistent storage but never emits an event, so off-chain consumers cannot
observe the escrow lifecycle start. Update create_escrow in lib.rs to publish a
dedicated escrow-created/pending event immediately after saving the
EscrowRecord, using the same event pattern as fund_escrow and
cancel_before_funding so indexers can reconstruct the full lifecycle from
create_escrow through later transitions.
- Line 525: The `timeout_ledger` calculation can overflow when adding
`timeout_ledgers` to `env.ledger().sequence()`, so replace the direct addition
with a checked or saturating add in both this path and the `deposit` flow.
Update the logic around `timeout_ledger` so it handles large `timeout_ledgers`
safely, and use the relevant helpers in the escrow contract’s timeout/deposit
code paths to prevent wraparound.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 73cdc067-9412-439b-8300-e70c553ff621

📥 Commits

Reviewing files that changed from the base of the PR and between b3d63c6 and 6d07fee.

📒 Files selected for processing (2)
  • contracts/escrow/src/integration_tests.rs
  • contracts/escrow/src/lib.rs

@ScriptedBro

Copy link
Copy Markdown
Contributor

Please resolve conflicts

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.

[Soroban/Escrow] Add Merchant Cancellation Before Funding

3 participants