Escrow & Settlement Contract

The escrow contract handles the critical lock-release cycle for off-ramp settlements.

Interface

rust
#[contract]
pub struct Escrow;

#[contractimpl]
impl Escrow {
    /// Lock funds in escrow for off-ramp settlement
    pub fn lock(
        env: Env,
        user: Address,
        amount: i128,
        request_id: BytesN<32>,  // Unique off-ramp request ID
    ) {
        user.require_auth();
        // Transfer USDC from user/vault to escrow
        token::transfer(&env, &user, &env.current_contract_address(), amount);
        // Store escrow record
        Self::store_escrow(&env, request_id, EscrowRecord {
            user: user.clone(),
            amount,
            status: EscrowStatus::Locked,
            locked_at: env.ledger().timestamp(),
        });
        env.events().publish(
            (symbol_short!("offramp"), symbol_short!("locked")),
            (user, amount, request_id),
        );
    }

    /// Release funds to LP after successful fiat settlement
    /// Called by authorized backend settlement service
    pub fn release_after_settlement(
        env: Env,
        authority: Address,
        request_id: BytesN<32>,
        lp_address: Address,
    ) {
        authority.require_auth();
        Self::verify_settlement_authority(&env, &authority);

        let record = Self::get_escrow(&env, request_id);
        assert!(record.status == EscrowStatus::Locked, "not locked");

        // Release USDC to liquidity provider
        token::transfer(&env, &env.current_contract_address(), &lp_address, record.amount);

        Self::update_status(&env, request_id, EscrowStatus::Settled);
        env.events().publish(
            (symbol_short!("offramp"), symbol_short!("settled")),
            (record.user, record.amount, request_id),
        );
    }

    /// Refund funds to user if settlement fails
    pub fn refund_if_failed(
        env: Env,
        authority: Address,
        request_id: BytesN<32>,
    ) {
        authority.require_auth();
        Self::verify_settlement_authority(&env, &authority);

        let record = Self::get_escrow(&env, request_id);
        assert!(record.status == EscrowStatus::Locked, "not locked");

        // Return USDC to user
        token::transfer(&env, &env.current_contract_address(), &record.user, record.amount);

        Self::update_status(&env, request_id, EscrowStatus::Refunded);
        env.events().publish(
            (symbol_short!("offramp"), symbol_short!("refunded")),
            (record.user, record.amount, request_id),
        );
    }
}

State Machine

  ┌──────────┐
  │  Locked   │ ← User initiates off-ramp
  └────┬──────┘
       │
       ├──── Fiat transfer succeeds ───▶ ┌──────────┐
       │                                  │ Settled  │
       │                                  └──────────┘
       │
       └──── Fiat transfer fails ──────▶ ┌──────────┐
              or timeout (30 min)         │ Refunded │
                                          └──────────┘