Skip to content

Commit cdf37e0

Browse files
authored
Merge pull request #742 from CodedBay/fix/issue-695-velocity-window-validation
fix(#695): reject velocity_limit > 0 when velocity_window == 0
2 parents fb61a94 + cc1ef0b commit cdf37e0

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

contracts/split/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5443,6 +5443,12 @@ impl SplitContract {
54435443
env.panic_with_error(e);
54445444
}
54455445
assert!(min_funding_bps <= 10_000, "min_funding_bps must be ≤ 10000");
5446+
// Issue #695: velocity_limit > 0 paired with velocity_window == 0 would cause
5447+
// division-by-zero or undefined behaviour when computing per-payer spend rates.
5448+
// Reject the configuration before any storage is written.
5449+
if velocity_limit > 0 && velocity_window == 0 {
5450+
panic_with_error!(env, ContractError::InvalidAmount);
5451+
}
54465452
assert_valid_bps(tax_bps).expect("tax_bps must be ≤ 10000");
54475453
assert_valid_bps(insurance_premium_bps).expect("insurance_premium_bps must be ≤ 10000");
54485454
// Issue #489 / #696: early-bird discounted platform fee must not exceed the

contracts/split/src/test.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2907,6 +2907,74 @@ fn test_min_funding_bps_allows_release_above_threshold() {
29072907
assert_eq!(tk.balance(&recipient), 900);
29082908
}
29092909

2910+
// ---------------------------------------------------------------------------
2911+
// Issue #695: validate velocity_window is non-zero when velocity_limit is set
2912+
// ---------------------------------------------------------------------------
2913+
2914+
#[test]
2915+
#[should_panic]
2916+
fn test_velocity_limit_without_window_rejected() {
2917+
// velocity_limit=1000 with velocity_window=0 must be rejected with ContractError::InvalidAmount.
2918+
let (env, contract_id, token_id) = setup_initialized();
2919+
let c = client(&env, &contract_id);
2920+
2921+
let creator = Address::generate(&env);
2922+
let recipient = Address::generate(&env);
2923+
2924+
env.ledger().set_timestamp(1_000);
2925+
2926+
let mut recipients = Vec::new(&env);
2927+
recipients.push_back(recipient.clone());
2928+
let mut amounts = Vec::new(&env);
2929+
amounts.push_back(1_000_i128);
2930+
2931+
// velocity_limit > 0 but velocity_window == 0 — undefined behaviour; must panic.
2932+
c.create_invoice(
2933+
&creator,
2934+
&recipients,
2935+
&amounts,
2936+
&token_id,
2937+
&9_999_u64,
2938+
&InvoiceOptions {
2939+
velocity_limit: 1_000,
2940+
velocity_window: 0,
2941+
..default_options(&env)
2942+
},
2943+
);
2944+
}
2945+
2946+
#[test]
2947+
fn test_velocity_limit_with_valid_window_accepted() {
2948+
// velocity_limit=1000, velocity_window=3600 is a valid configuration and must be accepted.
2949+
let (env, contract_id, token_id) = setup_initialized();
2950+
let c = client(&env, &contract_id);
2951+
2952+
let creator = Address::generate(&env);
2953+
let recipient = Address::generate(&env);
2954+
2955+
env.ledger().set_timestamp(1_000);
2956+
2957+
let mut recipients = Vec::new(&env);
2958+
recipients.push_back(recipient.clone());
2959+
let mut amounts = Vec::new(&env);
2960+
amounts.push_back(1_000_i128);
2961+
2962+
// Both limit and window are set — valid; invoice creation must succeed.
2963+
let id = c.create_invoice(
2964+
&creator,
2965+
&recipients,
2966+
&amounts,
2967+
&token_id,
2968+
&9_999_u64,
2969+
&InvoiceOptions {
2970+
velocity_limit: 1_000,
2971+
velocity_window: 3_600,
2972+
..default_options(&env)
2973+
},
2974+
);
2975+
assert_eq!(c.get_invoice(&id).status, InvoiceStatus::Pending);
2976+
}
2977+
29102978
// ---------------------------------------------------------------------------
29112979
// Issue #85: generate_payment_proof
29122980
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)