Skip to content
Merged
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
2 changes: 2 additions & 0 deletions contracts/cntr/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[package]
name = "cntr"
version = "0.1.0"
edition = "2021"
version = "0.0.0"
version = "0.1.0"
edition = "2021"
Expand Down
53 changes: 53 additions & 0 deletions contracts/cntr/src/escrow_refund.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
pub fn calculate_refund_amount(
booking_amount_stroops: i128,
cancellation_hours_before_start: u64,
) -> i128 {
if cancellation_hours_before_start >= 48 {
booking_amount_stroops
} else if cancellation_hours_before_start >= 24 {
booking_amount_stroops / 2
} else {
0
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn full_refund_at_48h() {
assert_eq!(calculate_refund_amount(1_000_000, 48), 1_000_000);
}

#[test]
fn full_refund_beyond_48h() {
assert_eq!(calculate_refund_amount(1_000_000, 72), 1_000_000);
}

#[test]
fn half_refund_at_24h() {
assert_eq!(calculate_refund_amount(1_000_000, 24), 500_000);
}

#[test]
fn half_refund_at_47h() {
assert_eq!(calculate_refund_amount(1_000_000, 47), 500_000);
}

#[test]
fn no_refund_at_23h() {
assert_eq!(calculate_refund_amount(1_000_000, 23), 0);
}

#[test]
fn no_refund_at_0h() {
assert_eq!(calculate_refund_amount(1_000_000, 0), 0);
}

#[test]
fn integer_division_no_float() {
// odd amount: 1_000_001 / 2 = 500_000 (integer division)
assert_eq!(calculate_refund_amount(1_000_001, 24), 500_000);
}
}
54 changes: 54 additions & 0 deletions contracts/cntr/src/escrow_release.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
pub fn validate_escrow_release(
booking_status: &str,
dispute_window_expired: bool,
) -> Result<(), &'static str> {
if booking_status != "COMPLETED" {
return Err("Booking not completed");
}
if !dispute_window_expired {
return Err("Dispute window still active");
}
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn ok_when_completed_and_window_expired() {
assert_eq!(validate_escrow_release("COMPLETED", true), Ok(()));
}

#[test]
fn err_when_not_completed() {
assert_eq!(
validate_escrow_release("PENDING", true),
Err("Booking not completed")
);
}

#[test]
fn err_when_dispute_window_active() {
assert_eq!(
validate_escrow_release("COMPLETED", false),
Err("Dispute window still active")
);
}

#[test]
fn err_when_both_conditions_fail_returns_not_completed() {
assert_eq!(
validate_escrow_release("PENDING", false),
Err("Booking not completed")
);
}

#[test]
fn err_for_cancelled_status() {
assert_eq!(
validate_escrow_release("CANCELLED", true),
Err("Booking not completed")
);
}
}
4 changes: 4 additions & 0 deletions contracts/cntr/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
pub mod subscription_expiry;
pub mod member_tier;
pub mod escrow_release;
pub mod escrow_refund;
pub mod credit_deduction;
pub mod referral_reward;
pub mod role_checker;
Expand Down
64 changes: 64 additions & 0 deletions contracts/cntr/src/member_tier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#[derive(Debug, PartialEq, Clone)]
pub enum MemberTier {
Bronze,
Silver,
Gold,
Platinum,
}

pub fn calculate_tier(total_bookings: u32, total_spend_stroops: i128) -> MemberTier {
if total_bookings >= 100 || total_spend_stroops >= 100_000_000 {
MemberTier::Platinum
} else if total_bookings >= 30 || total_spend_stroops >= 20_000_000 {
MemberTier::Gold
} else if total_bookings >= 10 || total_spend_stroops >= 5_000_000 {
MemberTier::Silver
} else {
MemberTier::Bronze
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn bronze_by_default() {
assert_eq!(calculate_tier(0, 0), MemberTier::Bronze);
}

#[test]
fn silver_at_10_bookings() {
assert_eq!(calculate_tier(10, 0), MemberTier::Silver);
}

#[test]
fn silver_by_spend() {
assert_eq!(calculate_tier(0, 5_000_000), MemberTier::Silver);
}

#[test]
fn gold_at_30_bookings() {
assert_eq!(calculate_tier(30, 0), MemberTier::Gold);
}

#[test]
fn gold_by_spend() {
assert_eq!(calculate_tier(0, 20_000_000), MemberTier::Gold);
}

#[test]
fn platinum_at_100_bookings() {
assert_eq!(calculate_tier(100, 0), MemberTier::Platinum);
}

#[test]
fn platinum_by_spend() {
assert_eq!(calculate_tier(0, 100_000_000), MemberTier::Platinum);
}

#[test]
fn below_silver_threshold() {
assert_eq!(calculate_tier(9, 4_999_999), MemberTier::Bronze);
}
}
45 changes: 45 additions & 0 deletions contracts/cntr/src/subscription_expiry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
pub fn is_subscription_expired(expiry_timestamp: u64, current_timestamp: u64) -> bool {
current_timestamp >= expiry_timestamp
}

pub fn days_until_expiry(expiry_timestamp: u64, current_timestamp: u64) -> i64 {
let diff = expiry_timestamp as i64 - current_timestamp as i64;
diff / 86400
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn expired_when_current_past_expiry() {
assert!(is_subscription_expired(100, 200));
}

#[test]
fn not_expired_when_current_before_expiry() {
assert!(!is_subscription_expired(200, 100));
}

#[test]
fn expired_at_boundary() {
assert!(is_subscription_expired(100, 100));
}

#[test]
fn days_until_positive_when_not_expired() {
// 10 days in the future
assert_eq!(days_until_expiry(864000, 0), 10);
}

#[test]
fn days_until_negative_when_expired() {
// expired 1 day ago
assert_eq!(days_until_expiry(0, 86400), -1);
}

#[test]
fn days_until_zero_at_boundary() {
assert_eq!(days_until_expiry(100, 100), 0);
}
}
Loading