forked from Ledger-Lenz/Ledgerlens-contract
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstorage.rs
More file actions
189 lines (150 loc) · 8.08 KB
/
Copy pathstorage.rs
File metadata and controls
189 lines (150 loc) · 8.08 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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
use soroban_sdk::{Address, Env, Symbol, Vec};
use crate::constants::{
DEFAULT_RISK_THRESHOLD, HISTORY_MAX_DEPTH, SCORE_TTL_EXTEND_TO, SCORE_TTL_THRESHOLD,
};
use crate::types::{AggregateRiskScore, DataKey, RiskScore};
// ── Admin / Service ─────────────────────────────────────────────────────────
pub fn has_admin(env: &Env) -> bool {
env.storage().instance().has(&DataKey::Admin)
}
pub fn set_admin(env: &Env, admin: &Address) {
env.storage().instance().set(&DataKey::Admin, admin);
}
pub fn get_admin(env: &Env) -> Address {
env.storage().instance().get(&DataKey::Admin).unwrap()
}
pub fn set_service(env: &Env, service: &Address) {
env.storage().instance().set(&DataKey::Service, service);
}
pub fn get_service(env: &Env) -> Address {
env.storage().instance().get(&DataKey::Service).unwrap()
}
// ── Latest score ─────────────────────────────────────────────────────────────
pub fn set_score(env: &Env, wallet: &Address, asset_pair: &Symbol, score: &RiskScore) {
let key = DataKey::Score(wallet.clone(), asset_pair.clone());
env.storage().persistent().set(&key, score);
env.storage().persistent().extend_ttl(&key, SCORE_TTL_THRESHOLD, SCORE_TTL_EXTEND_TO);
}
pub fn get_score(env: &Env, wallet: &Address, asset_pair: &Symbol) -> Option<RiskScore> {
let key = DataKey::Score(wallet.clone(), asset_pair.clone());
let score: Option<RiskScore> = env.storage().persistent().get(&key);
if score.is_some() {
env.storage().persistent().extend_ttl(&key, SCORE_TTL_THRESHOLD, SCORE_TTL_EXTEND_TO);
}
score
}
// ── Pause circuit breaker ────────────────────────────────────────────────────
pub fn is_paused(env: &Env) -> bool {
let result: Option<bool> = env.storage().instance().get(&DataKey::Paused);
result.unwrap_or(false)
}
pub fn set_paused(env: &Env, paused: bool) {
env.storage().instance().set(&DataKey::Paused, &paused);
}
// ── Two-step admin transfer ──────────────────────────────────────────────────
pub fn has_pending_admin(env: &Env) -> bool {
env.storage().instance().has(&DataKey::PendingAdmin)
}
pub fn set_pending_admin(env: &Env, new_admin: &Address) {
env.storage().instance().set(&DataKey::PendingAdmin, new_admin);
}
pub fn get_pending_admin(env: &Env) -> Option<Address> {
env.storage().instance().get(&DataKey::PendingAdmin)
}
pub fn clear_pending_admin(env: &Env) {
env.storage().instance().remove(&DataKey::PendingAdmin);
}
// ── Watchlist ────────────────────────────────────────────────────────────────
pub fn is_watchlisted(env: &Env, wallet: &Address) -> bool {
let result: Option<bool> = env.storage().persistent().get(&DataKey::Watchlist(wallet.clone()));
result.unwrap_or(false)
}
pub fn set_watchlist(env: &Env, wallet: &Address, flagged: bool) {
let key = DataKey::Watchlist(wallet.clone());
if flagged {
env.storage().persistent().set(&key, &true);
env.storage().persistent().extend_ttl(&key, SCORE_TTL_THRESHOLD, SCORE_TTL_EXTEND_TO);
} else {
env.storage().persistent().remove(&key);
}
}
// ── Risk threshold ───────────────────────────────────────────────────────────
pub fn get_risk_threshold(env: &Env) -> u32 {
let result: Option<u32> = env.storage().instance().get(&DataKey::RiskThreshold);
result.unwrap_or(DEFAULT_RISK_THRESHOLD)
}
pub fn set_risk_threshold(env: &Env, threshold: u32) {
env.storage().instance().set(&DataKey::RiskThreshold, &threshold);
}
// ── Score history ring buffer ────────────────────────────────────────────────
pub fn push_score_history(env: &Env, wallet: &Address, asset_pair: &Symbol, score: &RiskScore) {
let key = DataKey::ScoreHistory(wallet.clone(), asset_pair.clone());
let mut history: Vec<RiskScore> =
env.storage().persistent().get(&key).unwrap_or_else(|| Vec::new(env));
history.push_back(score.clone());
// Evict oldest entry when the ring exceeds the depth cap.
while history.len() > HISTORY_MAX_DEPTH {
history.remove(0);
}
env.storage().persistent().set(&key, &history);
env.storage().persistent().extend_ttl(&key, SCORE_TTL_THRESHOLD, SCORE_TTL_EXTEND_TO);
}
pub fn get_score_history(env: &Env, wallet: &Address, asset_pair: &Symbol) -> Vec<RiskScore> {
let key = DataKey::ScoreHistory(wallet.clone(), asset_pair.clone());
let history: Vec<RiskScore> =
env.storage().persistent().get(&key).unwrap_or_else(|| Vec::new(env));
if !history.is_empty() {
env.storage().persistent().extend_ttl(&key, SCORE_TTL_THRESHOLD, SCORE_TTL_EXTEND_TO);
}
history
}
// ── Contract version ─────────────────────────────────────────────────────────
pub fn get_contract_version(env: &Env) -> u32 {
let result: Option<u32> = env.storage().instance().get(&DataKey::ContractVersion);
result.unwrap_or(crate::constants::CONTRACT_VERSION)
}
// ── Cross-asset aggregate risk ───────────────────────────────────────────────
/// Adds `asset_pair` to the wallet's tracked pair list if it isn't already
/// present. Idempotent — re-registering an existing pair is a no-op aside
/// from the TTL bump.
pub fn register_pair_for_wallet(env: &Env, wallet: &Address, asset_pair: &Symbol) {
let key = DataKey::AssetPairs(wallet.clone());
let mut pairs: Vec<Symbol> =
env.storage().persistent().get(&key).unwrap_or_else(|| Vec::new(env));
if !pairs.contains(asset_pair) {
pairs.push_back(asset_pair.clone());
env.storage().persistent().set(&key, &pairs);
}
env.storage().persistent().extend_ttl(&key, SCORE_TTL_THRESHOLD, SCORE_TTL_EXTEND_TO);
}
pub fn get_wallet_pairs(env: &Env, wallet: &Address) -> Vec<Symbol> {
let key = DataKey::AssetPairs(wallet.clone());
let pairs: Vec<Symbol> = env.storage().persistent().get(&key).unwrap_or_else(|| Vec::new(env));
if !pairs.is_empty() {
env.storage().persistent().extend_ttl(&key, SCORE_TTL_THRESHOLD, SCORE_TTL_EXTEND_TO);
}
pairs
}
/// Returns the configured weight for `asset_pair`, defaulting to `1` (a
/// simple, unweighted average) when the admin has not set one explicitly.
pub fn get_pair_weight(env: &Env, asset_pair: &Symbol) -> u32 {
let key = DataKey::PairWeight(asset_pair.clone());
let weight: Option<u32> = env.storage().persistent().get(&key);
if weight.is_some() {
env.storage().persistent().extend_ttl(&key, SCORE_TTL_THRESHOLD, SCORE_TTL_EXTEND_TO);
}
weight.unwrap_or(1)
}
pub fn set_pair_weight(env: &Env, asset_pair: &Symbol, weight: u32) {
let key = DataKey::PairWeight(asset_pair.clone());
env.storage().persistent().set(&key, &weight);
env.storage().persistent().extend_ttl(&key, SCORE_TTL_THRESHOLD, SCORE_TTL_EXTEND_TO);
}
/// Refreshes the cached aggregate snapshot at `AggregateScore(wallet)`.
/// This is a write-through cache only — `get_aggregate_score` always
/// recomputes from live per-pair scores rather than reading it back.
pub fn set_aggregate_score(env: &Env, wallet: &Address, aggregate: &AggregateRiskScore) {
let key = DataKey::AggregateScore(wallet.clone());
env.storage().persistent().set(&key, aggregate);
env.storage().persistent().extend_ttl(&key, SCORE_TTL_THRESHOLD, SCORE_TTL_EXTEND_TO);
}