-
Notifications
You must be signed in to change notification settings - Fork 94
Expand file tree
/
Copy pathlib.rs
More file actions
139 lines (111 loc) · 4.29 KB
/
lib.rs
File metadata and controls
139 lines (111 loc) · 4.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#![no_std]
use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Env, Symbol};
/// Storage keys for the contract.
#[contracttype]
#[derive(Clone)]
pub enum DataKey {
Analytics,
}
/// The core analytics data structure to track loan health.
#[contracttype]
#[derive(Clone, Default, Debug)]
pub struct AnalyticsData {
pub total_principal_lent: i128,
pub active_loans_count: u32,
pub completed_loans_count: u32,
pub defaulted_loans_count: u32,
}
#[contract]
pub struct LendingAnalyticsContract;
#[contractimpl]
impl LendingAnalyticsContract {
/// Updates the dashboard stats when a new loan is issued.
pub fn update_stats_on_new_loan(env: Env, amount: i128) {
let mut stats: AnalyticsData = env
.storage()
.instance()
.get(&DataKey::Analytics)
.unwrap_or_default();
stats.total_principal_lent += amount;
stats.active_loans_count += 1;
env.storage().instance().set(&DataKey::Analytics, &stats);
// Emit a Soroban Event for indexers (e.g., Mercury)
// Topics: ["loan", "new"] | Data: amount
env.events()
.publish((symbol_short!("loan"), symbol_short!("new")), amount);
}
/// Updates the dashboard stats when a loan is repaid or defaults.
pub fn update_stats_on_repayment(env: Env, is_default: bool) {
let mut stats: AnalyticsData = env
.storage()
.instance()
.get(&DataKey::Analytics)
.unwrap_or_default();
if stats.active_loans_count > 0 {
stats.active_loans_count -= 1;
}
if is_default {
stats.defaulted_loans_count += 1;
} else {
stats.completed_loans_count += 1;
}
env.storage().instance().set(&DataKey::Analytics, &stats);
// Emit a Soroban Event for indexers
// Topics: ["loan", "repay"] | Data: is_default
env.events().publish(
(symbol_short!("loan"), symbol_short!("repay")),
is_default,
);
}
/// Public view function to fetch current dashboard statistics.
pub fn get_dashboard_stats(env: Env) -> AnalyticsData {
env.storage()
.instance()
.get(&DataKey::Analytics)
.unwrap_or_default()
}
/// Helper logic returning the default rate in basis points.
/// Uses fixed-point math: 10000 basis points = 100.00%.
pub fn get_default_rate_bps(env: Env) -> u32 {
let stats = Self::get_dashboard_stats(env);
let total_resolved = stats.completed_loans_count + stats.defaulted_loans_count;
if total_resolved == 0 {
return 0;
}
// Cast to u64 to prevent overflow during multiplication before dividing
(((stats.defaulted_loans_count as u64) * 10_000) / (total_resolved as u64)) as u32
}
}
#[cfg(test)]
mod test {
use super::*;
use soroban_sdk::Env;
#[test]
fn test_analytics_flow() {
let env = Env::default();
let contract_id = env.register_contract(None, LendingAnalyticsContract);
let client = LendingAnalyticsContractClient::new(&env, &contract_id);
// 1. Check initial state
let initial_stats = client.get_dashboard_stats();
assert_eq!(initial_stats.total_principal_lent, 0);
assert_eq!(initial_stats.active_loans_count, 0);
// 2. Add a new loan
client.update_stats_on_new_loan(&1000);
let stats_after_loan = client.get_dashboard_stats();
assert_eq!(stats_after_loan.total_principal_lent, 1000);
assert_eq!(stats_after_loan.active_loans_count, 1);
// 3. Repay loan (not default)
client.update_stats_on_repayment(&false);
let stats_after_repay = client.get_dashboard_stats();
assert_eq!(stats_after_repay.active_loans_count, 0);
assert_eq!(stats_after_repay.completed_loans_count, 1);
assert_eq!(stats_after_repay.defaulted_loans_count, 0);
// 4. Default rate check should be 0 bps
assert_eq!(client.get_default_rate_bps(), 0);
// 5. Add another loan and default it
client.update_stats_on_new_loan(&2000);
client.update_stats_on_repayment(&true);
// Default rate should now be 50.00% -> 5000 bps
assert_eq!(client.get_default_rate_bps(), 5000);
}
}