diff --git a/contracts/split/src/lib.rs b/contracts/split/src/lib.rs index f5e1624..fd53150 100644 --- a/contracts/split/src/lib.rs +++ b/contracts/split/src/lib.rs @@ -5405,7 +5405,7 @@ impl SplitContract { deadline > env.ledger().timestamp(), "deadline must be in the future" ); - // Issue #430: creator-defined payment window. + // Issue #430 / #697: creator-defined payment window. if let Some(close_at) = payment_close_at { assert!( close_at < deadline, @@ -5413,10 +5413,9 @@ impl SplitContract { ); } if let (Some(open_at), Some(close_at)) = (payment_open_at, payment_close_at) { - assert!( - open_at < close_at, - "payment_open_at must be before payment_close_at" - ); + if open_at >= close_at { + env.panic_with_error(ContractError::InvalidAmount); + } } assert!(bonus_pool >= 0, "bonus_pool must be non-negative"); // Issue #690: `penalty_bps` is a fraction of a late payment; a value diff --git a/contracts/split/src/test.rs b/contracts/split/src/test.rs index ca0e831..8dc6bbc 100644 --- a/contracts/split/src/test.rs +++ b/contracts/split/src/test.rs @@ -8495,3 +8495,88 @@ fn test_create_invoice_valid_multisig_setup_ok() { let id = c.create_invoice(&creator, &recipients, &amounts, &token_id, &9_999_u64, &opts); assert!(id >= 1); } + +// --- Issue #697: payment_open_at must be strictly before payment_close_at when both are set --- + +#[test] +fn test_create_invoice_payment_open_at_greater_than_close_at_rejected() { + let (env, contract_id, token_id) = setup_initialized(); + let c = client(&env, &contract_id); + env.ledger().set_timestamp(50); + + let creator = Address::generate(&env); + let (recipients, amounts) = two_recipients(&env); + + let mut opts = default_options(&env); + opts.ext.payment_open_at = Some(100); + opts.ext.payment_close_at = Some(99); + + let res = c.try_create_invoice(&creator, &recipients, &amounts, &token_id, &9_999_u64, &opts); + assert_eq!(res, Err(Ok(ContractError::InvalidAmount.into()))); +} + +#[test] +fn test_create_invoice_payment_open_at_equal_to_close_at_rejected() { + let (env, contract_id, token_id) = setup_initialized(); + let c = client(&env, &contract_id); + env.ledger().set_timestamp(50); + + let creator = Address::generate(&env); + let (recipients, amounts) = two_recipients(&env); + + let mut opts = default_options(&env); + opts.ext.payment_open_at = Some(100); + opts.ext.payment_close_at = Some(100); + + let res = c.try_create_invoice(&creator, &recipients, &amounts, &token_id, &9_999_u64, &opts); + assert_eq!(res, Err(Ok(ContractError::InvalidAmount.into()))); +} + +#[test] +fn test_create_invoice_payment_open_at_less_than_close_at_ok() { + let (env, contract_id, token_id) = setup_initialized(); + let c = client(&env, &contract_id); + env.ledger().set_timestamp(50); + + let creator = Address::generate(&env); + let (recipients, amounts) = two_recipients(&env); + + let mut opts = default_options(&env); + opts.ext.payment_open_at = Some(99); + opts.ext.payment_close_at = Some(100); + + let id = c.create_invoice(&creator, &recipients, &amounts, &token_id, &9_999_u64, &opts); + assert!(id >= 1); +} + +#[test] +fn test_create_invoice_payment_window_only_one_or_none_ok() { + let (env, contract_id, token_id) = setup_initialized(); + let c = client(&env, &contract_id); + env.ledger().set_timestamp(50); + + let creator = Address::generate(&env); + let (recipients, amounts) = two_recipients(&env); + + // Only payment_open_at set + let mut opts_open_only = default_options(&env); + opts_open_only.ext.payment_open_at = Some(100); + opts_open_only.ext.payment_close_at = None; + let id1 = c.create_invoice(&creator, &recipients, &amounts, &token_id, &9_999_u64, &opts_open_only); + assert!(id1 >= 1); + + // Only payment_close_at set + let mut opts_close_only = default_options(&env); + opts_close_only.ext.payment_open_at = None; + opts_close_only.ext.payment_close_at = Some(100); + let id2 = c.create_invoice(&creator, &recipients, &amounts, &token_id, &9_999_u64, &opts_close_only); + assert!(id2 >= 1); + + // Both None + let mut opts_none = default_options(&env); + opts_none.ext.payment_open_at = None; + opts_none.ext.payment_close_at = None; + let id3 = c.create_invoice(&creator, &recipients, &amounts, &token_id, &9_999_u64, &opts_none); + assert!(id3 >= 1); +} +