Skip to content

feat: integrate on-chain USDC refund transfer to buyer#173

Merged
anonfedora merged 5 commits intoanonfedora:masterfrom
Daniel4000-dev:feature/on-chain-usdc-refunds
Apr 2, 2026
Merged

feat: integrate on-chain USDC refund transfer to buyer#173
anonfedora merged 5 commits intoanonfedora:masterfrom
Daniel4000-dev:feature/on-chain-usdc-refunds

Conversation

@Daniel4000-dev
Copy link
Copy Markdown
Contributor

@Daniel4000-dev Daniel4000-dev commented Mar 28, 2026

Summary

This PR integrates actual on-chain USDC token transfers for the refund flow within the EscrowManager contract. Previously, the refund logic only updated the contract state but did not physically move the tokens back to the buyer. This change ensures that when an escrow is refunded (either due to expiry or dispute resolution), the funds are correctly transferred from the contract back to the buyer's Stellar address.

Closes #153

Type of Change

  • Bug fix
  • New feature
  • Refactor / code cleanup
  • Docs / config update
  • Contract change (Soroban)
  • Breaking change (ABI Rename)

Changes Made

contracts/escrow-manager/src/refund.rs

  • Introduced a dedicated module for handling refund execution, including balance validation and token transfer logic.

contracts/escrow-manager/src/lib.rs

  • Integrated the refund module.
  • Updated refund_escrow (expiry-based) and resolve_dispute to use the new on-chain transfer logic.
  • Renamed the misleading DisputeDecision::RefundToLender to DisputeDecision::RefundToBuyer to reflect the correct recipient of the funds.
  • Updated documentation and internal tests to match the new behavior.

backend/src/indexer/handlers.rs

  • Updated indexer comments to remain consistent with the renamed DisputeDecision variant.

Testing

  • cargo test -p escrow-manager was initiated to verify core logic and existing tests pass with the renamed variant.
  • Manually verified balance check and transfer logic in refund.rs.
  • Verified that state transitions (status updated to Refunded) still occur correctly.

Contract Changes

  • ABI / interface changed: DisputeDecision enum variant renamed from RefundToLender to RefundToBuyer.

Checklist

  • Self-reviewed the diff
  • No secrets or private keys committed
  • Relevant docs updated (contract documentation strings updated)
  • PR title follows type(scope): description format: feat(escrow): integrate on-chain usdc refund transfers

Summary by CodeRabbit

  • Bug Fixes

    • Dispute and expiry refunds now return funds to the buyer rather than the lender.
  • Refactor

    • Refund logic consolidated into a single, dedicated refund flow for clearer state handling.
  • New Features

    • Refunds now verify the contract’s token balance and surface an insufficient-balance error when funds are lacking.
  • Tests

    • Updated tests to assert refunds increase the buyer’s balance.

@drips-wave
Copy link
Copy Markdown

drips-wave bot commented Mar 28, 2026

@Daniel4000-dev 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 Mar 28, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c0222768-e25c-45b2-8bb3-2fb713abc93f

📥 Commits

Reviewing files that changed from the base of the PR and between 50f4c25 and 51939f1.

📒 Files selected for processing (1)
  • contracts/escrow-manager/src/lib.rs

📝 Walkthrough

Walkthrough

Replaces inline token transfer logic with a new on-chain refund::process_refund that moves escrowed tokens to the buyer and marks the escrow refunded; renames dispute decision to RefundToBuyer; updates backend comment and unit tests accordingly.

Changes

Cohort / File(s) Summary
Backend Event Handler
backend/src/indexer/handlers.rs
Updated comment in handle_escrow_event for "esc_rslv" to state decision_raw == 1 corresponds to refunding the buyer; runtime logic unchanged.
Contract Core
contracts/escrow-manager/src/lib.rs
Added InsufficientBalance error variant, renamed DisputeDecision::RefundToLenderRefundToBuyer, replaced inline transfer code with refund::process_refund(...) calls in expiry and dispute flows, adjusted status persistence and unit tests.
Refund Module
contracts/escrow-manager/src/refund.rs
New exported pub fn process_refund(env: &Env, escrow: &mut Escrow, _escrow_id: u64) -> Result<(), ContractError>: instantiates token::Client, checks contract token balance, returns InsufficientBalance if short, otherwise transfers escrow.amount to escrow.buyer and sets escrow.status = Refunded.

Sequence Diagram

sequenceDiagram
    participant Contract as Escrow Contract
    participant Token as Token Client
    participant Storage as Storage
    participant Buyer as Buyer Account

    Contract->>Token: query_balance(contract_address, asset)
    Token-->>Contract: balance (n)
    Contract->>Contract: compare n >= escrow.amount
    alt insufficient
        Contract-->>Contract: return InsufficientBalance error
    else sufficient
        Contract->>Token: transfer(contract_address → escrow.buyer, amount)
        Token->>Buyer: deliver tokens
        Token-->>Contract: transfer confirmed
        Contract->>Storage: set escrow.status = Refunded
        Storage-->>Contract: persist OK
        Contract-->>Contract: return Ok(())
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

Poem

🐇 I hopped through ledgers neat and spry,

Found tokens waiting in a sigh,
If contract coffers held the key,
I’d push the coins back merrily,
A tiny hop — refund to thee.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and specifically describes the main change: integrating on-chain USDC refund transfers to the buyer, which is the central objective of this changeset.
Description check ✅ Passed The PR description is comprehensive and follows the template structure, including summary, type of change, detailed changes made, testing performed, contract changes, and completed checklist items.
Linked Issues check ✅ Passed The PR successfully implements all requirements from issue #153: token client import, token transfer invocation with insufficient balance error handling, and state updates to Refunded status for both expiry and dispute refund paths.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue #153 requirements: refund module implementation, token transfer logic, ABI rename for clarity, indexer comment updates, and test adjustments align with the core objective of enabling on-chain refunds.
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

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.

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

Caution

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

⚠️ Outside diff range comments (1)
backend/src/indexer/handlers.rs (1)

200-229: ⚠️ Potential issue | 🟠 Major

Handle esc_rfnd in this match too.

refund_escrow in contracts/escrow-manager/src/lib.rs still emits esc_rfnd, but this handler only marks refunds on esc_rslv. Expiry refunds will settle on-chain and remain stale in Postgres/WebSocket consumers.

Suggested fix
             "esc_rslv" => {
                 if let ScVal::Vec(Some(args)) = data {
                     if args.len() < 2 {
                         return Err(anyhow!("Invalid args length for esc_rslv"));
                     }
                     let id = scval_to_u64(&args[0])?;
                     let decision_raw = scval_to_u64(&args[1])?;

                     // DisputeDecision: 0=ReleaseToSeller, 1=RefundToBuyer
                     let new_status = match decision_raw {
                         0 => "released",
                         1 => "refunded",
                         _ => return Err(anyhow!("Invalid dispute decision value")),
                     };

                     sqlx::query(
                         "UPDATE escrows SET status = $1::escrow_status, disputed = false WHERE escrow_id = $2"
                     )
                     .bind(new_status)
                     .bind(id as i64)
                     .execute(&self.pool)
                     .await?;

                     if let Some(ws) = &self.ws_state {
                         match decision_raw {
                             0 => ws.broadcast_event(WsEscrowEvent::Released { escrow_id: id as i64 }).await,
                             1 => ws.broadcast_event(WsEscrowEvent::Refunded { escrow_id: id as i64 }).await,
                             _ => {}
                         }
                     }
                 }
             }
+            "esc_rfnd" => {
+                if let ScVal::Vec(Some(args)) = data {
+                    if args.is_empty() {
+                        return Err(anyhow!("Invalid args length for esc_rfnd"));
+                    }
+                    let id = scval_to_u64(&args[0])?;
+
+                    sqlx::query(
+                        "UPDATE escrows SET status = 'refunded'::escrow_status WHERE escrow_id = $1"
+                    )
+                    .bind(id as i64)
+                    .execute(&self.pool)
+                    .await?;
+
+                    if let Some(ws) = &self.ws_state {
+                        ws.broadcast_event(WsEscrowEvent::Refunded { escrow_id: id as i64 }).await;
+                    }
+                }
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/indexer/handlers.rs` around lines 200 - 229, The handler
currently only processes the "esc_rslv" event; add handling for the "esc_rfnd"
event (emitted by refund_escrow) so refunds update Postgres and notify WS
clients. Locate the match arm matching "esc_rslv" and either add a separate
"esc_rfnd" arm or merge both event strings to run the same logic: parse args
with scval_to_u64, set new_status to "refunded" for esc_rfnd (or map
decision_raw to "released"/"refunded" as done), run the same sqlx::query UPDATE
that sets status and disputed=false, and call
ws.broadcast_event(WsEscrowEvent::Refunded { escrow_id }) for refund cases (and
WsEscrowEvent::Released for release cases) so expiry refunds are reflected in
Postgres and WebSocket consumers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@contracts/escrow-manager/src/lib.rs`:
- Around line 1282-1283: The test still asserts the lender received +5000 after
calling t.escrow_client.resolve_dispute(&escrow_id,
&DisputeDecision::RefundToBuyer), which is incorrect; update the assertions in
this test and in test_refund_escrow_success to assert the buyer's balance
increases by the refunded amount instead of the lender's. Locate the
resolve_dispute call and the related balance checks (search for
DisputeDecision::RefundToBuyer, test_refund_escrow_success, and process_refund)
and swap the lender/buyer expectation so the buyer's balance reflects the +5000
refund and the lender's balance remains unchanged.

In `@contracts/escrow-manager/src/refund.rs`:
- Around line 18-19: The refund path returns ContractError::InsufficientBalance
but that variant doesn't exist; add a new variant named InsufficientBalance to
the ContractError enum (in the contract's lib where EscrowNotDisputed = 14 is
defined) and give it the next error code/value (e.g., = 15) to preserve existing
numeric codes, then recompile and update any use-sites if needed to ensure the
new variant is referenced where refund (in refund.rs) checks contract_balance
against escrow.amount.
- Around line 2-12: The code uses the wrong escrow type: change the parameter
and any references from TradeEscrow to the actual Escrow type defined in lib.rs
by importing Escrow (e.g., use crate::Escrow) and updating the process_refund
signature (env: &Env, escrow: &mut Escrow, escrow_id: u64) and any other
occurrences of TradeEscrow in this module to Escrow; also adjust field accesses
if the Escrow struct uses different field names than TradeEscrow.

---

Outside diff comments:
In `@backend/src/indexer/handlers.rs`:
- Around line 200-229: The handler currently only processes the "esc_rslv"
event; add handling for the "esc_rfnd" event (emitted by refund_escrow) so
refunds update Postgres and notify WS clients. Locate the match arm matching
"esc_rslv" and either add a separate "esc_rfnd" arm or merge both event strings
to run the same logic: parse args with scval_to_u64, set new_status to
"refunded" for esc_rfnd (or map decision_raw to "released"/"refunded" as done),
run the same sqlx::query UPDATE that sets status and disputed=false, and call
ws.broadcast_event(WsEscrowEvent::Refunded { escrow_id }) for refund cases (and
WsEscrowEvent::Released for release cases) so expiry refunds are reflected in
Postgres and WebSocket consumers.
🪄 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: 2e502553-4fb6-45c7-8e12-90d91c27d6e1

📥 Commits

Reviewing files that changed from the base of the PR and between 4b3093b and 6581445.

📒 Files selected for processing (3)
  • backend/src/indexer/handlers.rs
  • contracts/escrow-manager/src/lib.rs
  • contracts/escrow-manager/src/refund.rs

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.

🧹 Nitpick comments (2)
contracts/escrow-manager/src/lib.rs (2)

616-634: Reuse unlock_collateral and the helper-owned status update.

contracts/escrow-manager/src/refund.rs:9-34 already marks the escrow as refunded, so this branch is now the only refund path that both rewrites the same status and reimplements collateral unlocking inline. Folding it down to Self::unlock_collateral keeps the expiry refund flow aligned with the dispute refund flow and removes duplicated logic.

♻️ Proposed cleanup
-        // Refund using the new refund module
-        refund::process_refund(&env, &mut escrow, escrow_id)?;
-
-        // Unlock collateral via CollateralRegistry
-        let coll_reg: Address = env
-            .storage()
-            .instance()
-            .get(&symbol_short!("coll_reg"))
-            .ok_or(ContractError::Unauthorized)?;
-
-        let unlock_args: Vec<Val> = Vec::from_array(&env, [escrow.collateral_id.into_val(&env)]);
-        env.invoke_contract::<Val>(
-            &coll_reg,
-            &Symbol::new(&env, "unlock_collateral"),
-            unlock_args,
-        );
-
-        escrow.status = EscrowStatus::Refunded;
+        refund::process_refund(&env, &mut escrow, escrow_id)?;
+        Self::unlock_collateral(&env, escrow.collateral_id)?;
         env.storage().persistent().set(&escrow_id, &escrow);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/escrow-manager/src/lib.rs` around lines 616 - 634, This branch
duplicates logic already in refund::process_refund by manually invoking the
collateral registry and setting escrow.status; replace the inline collateral
unlocking and status rewrite with a single call to Self::unlock_collateral so
the expiry refund path reuses the same helper used by the dispute refund
flow—locate the block that calls refund::process_refund(&env, &mut escrow,
escrow_id)? and remove the manual coll_reg lookup, env.invoke_contract
unlock_collateral call, and escrow.status assignment, then call
Self::unlock_collateral(&env, &mut escrow, escrow_id) (or the appropriate method
signature) to perform unlocking and status update.

48-48: Add a regression test for InsufficientBalance.

This introduces a new public error branch, but the test module still doesn’t exercise it through either refund entry point. A failing-path test that drives the escrow contract’s token balance below escrow.amount before refund would make this guard much harder to regress.

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

In `@contracts/escrow-manager/src/lib.rs` at line 48, Add a regression test that
triggers the new InsufficientBalance error by creating an escrow whose
escrow.amount is greater than the contract's actual token balance at refund
time: in the test module create an escrow, drain or transfer tokens from the
contract so the contract token balance < escrow.amount, then call the refund
entry point(s) and assert the call fails with the InsufficientBalance error;
repeat for both refund entry points if there are multiple to ensure the new
public error branch is exercised.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@contracts/escrow-manager/src/lib.rs`:
- Around line 616-634: This branch duplicates logic already in
refund::process_refund by manually invoking the collateral registry and setting
escrow.status; replace the inline collateral unlocking and status rewrite with a
single call to Self::unlock_collateral so the expiry refund path reuses the same
helper used by the dispute refund flow—locate the block that calls
refund::process_refund(&env, &mut escrow, escrow_id)? and remove the manual
coll_reg lookup, env.invoke_contract unlock_collateral call, and escrow.status
assignment, then call Self::unlock_collateral(&env, &mut escrow, escrow_id) (or
the appropriate method signature) to perform unlocking and status update.
- Line 48: Add a regression test that triggers the new InsufficientBalance error
by creating an escrow whose escrow.amount is greater than the contract's actual
token balance at refund time: in the test module create an escrow, drain or
transfer tokens from the contract so the contract token balance < escrow.amount,
then call the refund entry point(s) and assert the call fails with the
InsufficientBalance error; repeat for both refund entry points if there are
multiple to ensure the new public error branch is exercised.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 47056fdf-cb74-4963-9632-c76be6c51bd6

📥 Commits

Reviewing files that changed from the base of the PR and between 6581445 and 7008810.

📒 Files selected for processing (2)
  • contracts/escrow-manager/src/lib.rs
  • contracts/escrow-manager/src/refund.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/escrow-manager/src/refund.rs

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.

🧹 Nitpick comments (1)
contracts/escrow-manager/src/lib.rs (1)

614-632: Redundant status assignment.

refund::process_refund already sets escrow.status = EscrowStatus::Refunded internally (line 32 in refund.rs). The explicit assignment on line 631 is redundant. While harmless, it creates inconsistency with resolve_dispute's RefundToBuyer branch which omits the explicit assignment.

Consider removing the redundant line for consistency:

♻️ Proposed fix
         // Refund using the new refund module
         refund::process_refund(&env, &mut escrow, escrow_id)?;
 
         // Unlock collateral via CollateralRegistry
         let coll_reg: Address = env
             .storage()
             .instance()
             .get(&symbol_short!("coll_reg"))
             .ok_or(ContractError::Unauthorized)?;
 
         let unlock_args: Vec<Val> = Vec::from_array(&env, [escrow.collateral_id.into_val(&env)]);
         env.invoke_contract::<Val>(
             &coll_reg,
             &Symbol::new(&env, "unlock_collateral"),
             unlock_args,
         );
 
-        escrow.status = EscrowStatus::Refunded;
         env.storage().persistent().set(&escrow_id, &escrow);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/escrow-manager/src/lib.rs` around lines 614 - 632, Remove the
redundant explicit reassignment of escrow.status to EscrowStatus::Refunded after
calling refund::process_refund: refund::process_refund(&env, &mut escrow,
escrow_id) already sets the status, so delete the line "escrow.status =
EscrowStatus::Refunded;" to match the resolve_dispute RefundToBuyer branch and
avoid duplicate state updates; ensure escrow is still persisted with
env.storage().persistent().set(&escrow_id, &escrow) after the refund call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@contracts/escrow-manager/src/lib.rs`:
- Around line 614-632: Remove the redundant explicit reassignment of
escrow.status to EscrowStatus::Refunded after calling refund::process_refund:
refund::process_refund(&env, &mut escrow, escrow_id) already sets the status, so
delete the line "escrow.status = EscrowStatus::Refunded;" to match the
resolve_dispute RefundToBuyer branch and avoid duplicate state updates; ensure
escrow is still persisted with env.storage().persistent().set(&escrow_id,
&escrow) after the refund call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6da3eca2-770d-4fee-999d-8f06dd33b685

📥 Commits

Reviewing files that changed from the base of the PR and between 7008810 and 0d11b6d.

📒 Files selected for processing (1)
  • contracts/escrow-manager/src/lib.rs

@anonfedora
Copy link
Copy Markdown
Owner

HI @Daniel4000-dev, can you fix failing contract CI so I can merge?

@Daniel4000-dev
Copy link
Copy Markdown
Contributor Author

Daniel4000-dev commented Apr 1, 2026

resolved

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.

🧹 Nitpick comments (2)
contracts/escrow-manager/src/refund.rs (2)

9-13: Remove unused _escrow_id from the helper signature (or use it).

The parameter is currently dead and adds unnecessary API surface.

🧹 Suggested cleanup
 pub fn process_refund(
     env: &Env,
-    escrow: &mut Escrow,
-    _escrow_id: u64,
+    escrow: &mut Escrow,
 ) -> Result<(), ContractError> {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/escrow-manager/src/refund.rs` around lines 9 - 13, The
process_refund function currently declares an unused parameter _escrow_id which
adds dead API surface; update the signature of process_refund (and any callers)
to remove the _escrow_id parameter from fn process_refund(env: &Env, escrow:
&mut Escrow, _escrow_id: u64) -> Result<(), ContractError> or alternatively use
the value inside the function if it is required for logic (e.g., referencing
escrow ID for logging or validation); ensure you also update all call sites to
match the new signature and run tests/compile to confirm no references remain.

31-32: Centralize refund status ownership to avoid split behavior.

process_refund sets escrow.status, but refund_escrow in contracts/escrow-manager/src/lib.rs sets it again before persistence. Prefer a single owner for status transition to reduce drift between call sites.

♻️ Suggested refactor (make entrypoints own status + persistence)
-    // 3. Update the state
-    escrow.status = EscrowStatus::Refunded;
-
     Ok(())

Then set EscrowStatus::Refunded in each entrypoint right before env.storage().persistent().set(...).

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

In `@contracts/escrow-manager/src/refund.rs` around lines 31 - 32, process_refund
currently mutates escrow.status to EscrowStatus::Refunded while refund_escrow in
lib.rs also sets the status before persisting, causing split ownership; remove
the status assignment from process_refund and instead make each entrypoint
(e.g., refund_escrow) set escrow.status = EscrowStatus::Refunded immediately
before calling env.storage().persistent().set(...), so the entrypoint owns the
status transition and persistence consistently (refer to process_refund,
refund_escrow, EscrowStatus::Refunded and env.storage().persistent().set to
locate the changes).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@contracts/escrow-manager/src/refund.rs`:
- Around line 9-13: The process_refund function currently declares an unused
parameter _escrow_id which adds dead API surface; update the signature of
process_refund (and any callers) to remove the _escrow_id parameter from fn
process_refund(env: &Env, escrow: &mut Escrow, _escrow_id: u64) -> Result<(),
ContractError> or alternatively use the value inside the function if it is
required for logic (e.g., referencing escrow ID for logging or validation);
ensure you also update all call sites to match the new signature and run
tests/compile to confirm no references remain.
- Around line 31-32: process_refund currently mutates escrow.status to
EscrowStatus::Refunded while refund_escrow in lib.rs also sets the status before
persisting, causing split ownership; remove the status assignment from
process_refund and instead make each entrypoint (e.g., refund_escrow) set
escrow.status = EscrowStatus::Refunded immediately before calling
env.storage().persistent().set(...), so the entrypoint owns the status
transition and persistence consistently (refer to process_refund, refund_escrow,
EscrowStatus::Refunded and env.storage().persistent().set to locate the
changes).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 005be1bc-4f20-4b2f-a78c-4415d4894c76

📥 Commits

Reviewing files that changed from the base of the PR and between 0d11b6d and 50f4c25.

📒 Files selected for processing (1)
  • contracts/escrow-manager/src/refund.rs

@anonfedora anonfedora merged commit 7d41458 into anonfedora:master Apr 2, 2026
3 of 5 checks passed
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.

[RefundManager] process_refund: Integrate actual USDC token transfer on-chain**

2 participants