Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 50 additions & 3 deletions contracts/escrow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ pub struct DisputeRaisedEvent {
#[derive(Clone)]
pub struct DepositEvent {
pub job_id: u64,
pub deposited_by: Address,
pub token: Address,
pub amount: i128,
pub milestone_count: u32,
pub deposited_at: u64,
}
#[contracttype]
Expand Down Expand Up @@ -485,10 +488,13 @@ impl EscrowContract {
// Emit deposit event for off-chain logging
let evt = DepositEvent {
job_id,
deposited_by: job.client.clone(),
token: job.token.clone(),
amount,
milestone_count: job.milestones.len(),
deposited_at: env.ledger().timestamp(),
};
env.events().publish(("escrow", "Deposit"), evt);
env.events().publish(("escrow", "DepositEvent"), evt);

Ok(())
}
Expand Down Expand Up @@ -867,8 +873,8 @@ impl EscrowContract {
#[cfg(test)]
mod test {
use super::*;
use soroban_sdk::testutils::Address as _;
use soroban_sdk::{token, Address, Env};
use soroban_sdk::testutils::{Address as _, Events as _};
use soroban_sdk::{token, Address, Env, IntoVal};

fn setup_token(env: &Env, admin: &Address) -> Address {
let contract = env.register_stellar_asset_contract_v2(admin.clone());
Expand Down Expand Up @@ -969,6 +975,47 @@ mod test {
assert_eq!(job.status, EscrowStatus::Completed);
}

#[test]
fn test_deposit_emits_deposit_event() {
let env = Env::default();
env.mock_all_auths();

let admin = Address::generate(&env);
let agent_judge = Address::generate(&env);
let client = Address::generate(&env);
let freelancer = Address::generate(&env);

let token_addr = setup_token(&env, &admin);
mint(&env, &token_addr, &client);

let contract_id = env.register_contract(None, EscrowContract);
let cc = EscrowContractClient::new(&env, &contract_id);

cc.initialize(&admin, &agent_judge);
cc.create_job(&87u64, &client, &freelancer, &token_addr);
cc.add_milestone(&87u64, &1500i128);
cc.add_milestone(&87u64, &2500i128);

cc.deposit(&87u64, &4000i128);

let events = env.events().all();
let deposit_event = events.get(events.len() - 1).unwrap();
assert_eq!(deposit_event.0, contract_id);
assert_eq!(deposit_event.1, ("escrow", "DepositEvent").into_val(&env));
assert_eq!(
deposit_event.2,
DepositEvent {
job_id: 87,
deposited_by: client,
token: token_addr,
amount: 4000,
milestone_count: 2,
deposited_at: env.ledger().timestamp(),
}
.into_val(&env)
);
}

#[test]
// Initialization now returns EscrowError::AlreadyInitialized which surfaces
// as a host error with numeric code #1. Match that in the test.
Expand Down
35 changes: 35 additions & 0 deletions docs/contracts/escrow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Escrow Smart Contract

## Overview

The `Escrow` contract manages milestone funding, release, refund, and dispute flows for Lance jobs.

## `DepositEvent`

### Purpose

`DepositEvent` is emitted by `deposit` after the client funds an escrow job and the job transitions to `Funded`. It gives backend indexers and clients a durable audit signal for funded jobs.

### Topic

- `("escrow", "DepositEvent")`

### Payload

- `job_id`: Unique job identifier.
- `deposited_by`: Client address that authorized and funded the escrow.
- `token`: Token contract address transferred into escrow.
- `amount`: Total amount deposited.
- `milestone_count`: Number of milestones funded by the deposit.
- `deposited_at`: Ledger timestamp when the event was emitted.

### Validation

`deposit` emits the event only after the existing escrow checks and state changes succeed:

- The job must exist and be in `Setup`.
- The caller is authenticated through the stored client address.
- The deposited amount must be positive.
- The job must have at least one milestone.
- The sum of milestone amounts must match the deposit amount.
- Token transfer into the contract and persistent job update must complete successfully.
Loading