diff --git a/contracts/invoice-escrow/src/errors.rs b/contracts/invoice-escrow/src/errors.rs index 53efb91..ab669da 100644 --- a/contracts/invoice-escrow/src/errors.rs +++ b/contracts/invoice-escrow/src/errors.rs @@ -104,4 +104,4 @@ pub enum Error { InsufficientRepayment = 44, /// New funding deadline must be greater than the current deadline. DeadlineNotExtended = 45, -} \ No newline at end of file +} diff --git a/contracts/invoice-escrow/src/events.rs b/contracts/invoice-escrow/src/events.rs index 30408a1..f527c23 100644 --- a/contracts/invoice-escrow/src/events.rs +++ b/contracts/invoice-escrow/src/events.rs @@ -274,4 +274,4 @@ pub fn settlement_paid( (Symbol::new(env, "settlement_paid"),), (investor.clone(), invoice_id.clone(), payout_amount, yield_earned), ); -} \ No newline at end of file +} diff --git a/contracts/invoice-escrow/src/lib.rs b/contracts/invoice-escrow/src/lib.rs index c654064..5e49959 100644 --- a/contracts/invoice-escrow/src/lib.rs +++ b/contracts/invoice-escrow/src/lib.rs @@ -75,11 +75,24 @@ impl InvoiceEscrow { paused: false, whitelist_enabled: false, min_investment: 0, + grace_period_seconds: 0, }; storage::set_config(&env, &config); Ok(()) } + /// Admin-only: set the grace period window for overdue invoice settlement. + pub fn set_grace_period(env: Env, admin: Address, grace_period_seconds: u64) -> Result<(), Error> { + admin.require_auth(); + let mut config = storage::get_config(&env).ok_or(Error::NotInit)?; + if config.admin != admin { + return Err(Error::Unauthorized); + } + config.grace_period_seconds = grace_period_seconds; + storage::set_config(&env, &config); + Ok(()) + } + /// Admin-only: set the minimum investment amount for `fund_escrow`. /// Pass `0` to disable the floor (deposits must still be strictly positive). pub fn set_min_investment(env: Env, admin: Address, min_investment: i128) -> Result<(), Error> { @@ -734,8 +747,9 @@ impl InvoiceEscrow { return Err(Error::RefundNotAllowed); } let ledger_ts = env.ledger().timestamp(); - if ledger_ts < data.due_dt { - return Err(Error::RefundNotAllowed); + let final_deadline = data.due_dt.checked_add(config.grace_period_seconds).unwrap_or(u64::MAX); + if ledger_ts <= final_deadline { + return Err(Error::EscrowNotOverdue); } // Refund the remaining collateral (purchase_price minus already released partial payments) @@ -805,6 +819,7 @@ impl InvoiceEscrow { soroban_sdk::vec![&env, contract.to_val(), false.into_val(&env)], ); + events::grace_period_expired(&env, invoice_id.clone()); events::escrow_refunded(&env, invoice_id.clone(), amount_to_refund); events::escrow_status_changed( &env, diff --git a/contracts/invoice-escrow/src/types.rs b/contracts/invoice-escrow/src/types.rs index 8cd3362..2daa47c 100644 --- a/contracts/invoice-escrow/src/types.rs +++ b/contracts/invoice-escrow/src/types.rs @@ -76,6 +76,8 @@ pub struct Config { /// `0` disables the floor (only `amount > 0` is required). Completing the /// remaining capacity below this floor is always allowed. pub min_investment: i128, + /// Grace period in seconds for overdue invoice settlement. + pub grace_period_seconds: u64, } /// Lifecycle status of an escrow.