From d9aab2672b6cb10e7ee8599465cd882181f7d9f8 Mon Sep 17 00:00:00 2001 From: Gozirimdev Date: Thu, 23 Apr 2026 11:59:04 +0100 Subject: [PATCH] feat(escrow): emit deposit event --- contracts/escrow/src/lib.rs | 53 ++++++++++++++++++++++++++++++++++--- docs/contracts/escrow.md | 35 ++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 docs/contracts/escrow.md diff --git a/contracts/escrow/src/lib.rs b/contracts/escrow/src/lib.rs index 9413b0de..b3c83dca 100644 --- a/contracts/escrow/src/lib.rs +++ b/contracts/escrow/src/lib.rs @@ -92,7 +92,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, } #[derive(Clone)] @@ -262,10 +265,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(()) } @@ -556,8 +562,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()); @@ -658,6 +664,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. diff --git a/docs/contracts/escrow.md b/docs/contracts/escrow.md new file mode 100644 index 00000000..7c59ca7d --- /dev/null +++ b/docs/contracts/escrow.md @@ -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.