1use alloc::vec::Vec;
7
8use templar_vault_kernel::TargetId;
9
10use super::cap_group::{CapGroupError, CapGroupId, CapGroupRecord};
11use super::market_lock::MarketLeaseRegistry;
12use super::supply_queue::{SupplyQueue, SupplyQueueError};
13
14#[templar_vault_macros::vault_derive(borsh, borsh_schema, serde, postcard, schemars)]
15#[derive(Clone, PartialEq, Eq)]
16pub struct OrderedMap<K, V> {
17 entries: Vec<(K, V)>,
18}
19
20impl<K, V> Default for OrderedMap<K, V> {
21 fn default() -> Self {
22 Self {
23 entries: Vec::new(),
24 }
25 }
26}
27
28impl<K: PartialEq, V> OrderedMap<K, V> {
29 #[must_use]
30 pub fn new() -> Self {
31 Self::default()
32 }
33
34 #[must_use]
35 pub fn len(&self) -> usize {
36 self.entries.len()
37 }
38
39 #[must_use]
40 pub fn is_empty(&self) -> bool {
41 self.entries.is_empty()
42 }
43
44 pub fn clear(&mut self) {
45 self.entries.clear();
46 }
47
48 pub fn insert(&mut self, key: K, value: V) -> Option<V> {
49 if let Some((_, existing)) = self
50 .entries
51 .iter_mut()
52 .find(|(candidate, _)| candidate == &key)
53 {
54 return Some(core::mem::replace(existing, value));
55 }
56 self.entries.push((key, value));
57 None
58 }
59
60 #[must_use]
61 pub fn get(&self, key: &K) -> Option<&V> {
62 self.entries
63 .iter()
64 .find(|(candidate, _)| candidate == key)
65 .map(|(_, value)| value)
66 }
67
68 #[must_use]
69 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
70 self.entries
71 .iter_mut()
72 .find(|(candidate, _)| candidate == key)
73 .map(|(_, value)| value)
74 }
75
76 #[must_use]
77 pub fn contains_key(&self, key: &K) -> bool {
78 self.entries.iter().any(|(candidate, _)| candidate == key)
79 }
80
81 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
82 self.entries.iter().map(|(key, value)| (key, value))
83 }
84
85 pub fn retain(&mut self, mut f: impl FnMut(&K, &V) -> bool) {
86 self.entries.retain(|(key, value)| f(key, value));
87 }
88
89 pub fn remove(&mut self, key: &K) -> Option<V> {
90 let index = self
91 .entries
92 .iter()
93 .position(|(candidate, _)| candidate == key)?;
94 Some(self.entries.remove(index).1)
95 }
96
97 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
98 self.entries.iter_mut().map(|(key, value)| (&*key, value))
99 }
100
101 pub fn keys(&self) -> impl Iterator<Item = &K> {
102 self.entries.iter().map(|(key, _)| key)
103 }
104
105 pub fn values(&self) -> impl Iterator<Item = &V> {
106 self.entries.iter().map(|(_, value)| value)
107 }
108}
109
110impl<K: PartialEq, V> FromIterator<(K, V)> for OrderedMap<K, V> {
111 fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
112 let mut map = Self::default();
113 for (key, value) in iter {
114 let _ = map.insert(key, value);
115 }
116 map
117 }
118}
119
120impl<K, V> IntoIterator for OrderedMap<K, V> {
121 type Item = (K, V);
122 type IntoIter = alloc::vec::IntoIter<(K, V)>;
123
124 fn into_iter(self) -> Self::IntoIter {
125 self.entries.into_iter()
126 }
127}
128
129#[templar_vault_macros::vault_derive(borsh, serde)]
130#[derive(Clone, PartialEq, Eq)]
131pub struct MarketConfig {
132 pub enabled: bool,
133 pub cap: u128,
134 pub cap_group_id: Option<CapGroupId>,
135}
136
137impl MarketConfig {
138 #[must_use]
139 pub fn new(enabled: bool, cap: u128, cap_group_id: Option<CapGroupId>) -> Self {
140 Self {
141 enabled,
142 cap,
143 cap_group_id,
144 }
145 }
146}
147
148impl Default for MarketConfig {
149 fn default() -> Self {
150 Self {
151 enabled: true,
152 cap: 0,
153 cap_group_id: None,
154 }
155 }
156}
157
158#[templar_vault_macros::vault_derive(borsh, serde)]
160#[derive(Clone, Default)]
161pub struct PolicyState {
162 markets: OrderedMap<TargetId, MarketConfig>,
163 principals: OrderedMap<TargetId, u128>,
164 cap_groups: OrderedMap<CapGroupId, CapGroupRecord>,
165 supply_queue: SupplyQueue,
166 leases: MarketLeaseRegistry,
167}
168
169#[templar_vault_macros::vault_derive]
170#[derive(Clone, PartialEq, Eq)]
171pub enum PolicyStateError {
172 UnknownMarket { target_id: TargetId },
173 UnknownCapGroup { id: CapGroupId },
174 CapGroupInUse { id: CapGroupId },
175 PrincipalOverflow { target_id: TargetId },
176 InvalidSupplyQueue { source: SupplyQueueError },
177 SupplyQueueUnknownMarket { target_id: TargetId },
178 SupplyQueueDisabledMarket { target_id: TargetId },
179 SupplyQueueUnauthorizedMarket { target_id: TargetId },
180}
181
182impl PolicyState {
183 pub fn from_parts(
184 markets: OrderedMap<TargetId, MarketConfig>,
185 principals: OrderedMap<TargetId, u128>,
186 cap_groups: OrderedMap<CapGroupId, CapGroupRecord>,
187 leases: MarketLeaseRegistry,
188 supply_queue: SupplyQueue,
189 ) -> Result<Self, PolicyStateError> {
190 let mut state = Self {
191 markets,
192 principals,
193 cap_groups,
194 supply_queue,
195 leases,
196 };
197 state
198 .supply_queue
199 .validate()
200 .map_err(|source| PolicyStateError::InvalidSupplyQueue { source })?;
201 state.validate_supply_queue_targets()?;
202 state.prune_orphan_principals();
203 state.initialize_missing_principals();
204 state.recompute_cap_group_principals()?;
205 Ok(state)
206 }
207
208 #[must_use]
209 pub fn markets(&self) -> &OrderedMap<TargetId, MarketConfig> {
210 &self.markets
211 }
212
213 #[must_use]
214 pub fn principals(&self) -> &OrderedMap<TargetId, u128> {
215 &self.principals
216 }
217
218 #[must_use]
219 pub fn cap_groups(&self) -> &OrderedMap<CapGroupId, CapGroupRecord> {
220 &self.cap_groups
221 }
222
223 #[must_use]
224 pub fn supply_queue(&self) -> &SupplyQueue {
225 &self.supply_queue
226 }
227
228 #[must_use]
229 pub fn leases(&self) -> &MarketLeaseRegistry {
230 &self.leases
231 }
232
233 #[must_use]
234 pub fn market_config(&self, target_id: TargetId) -> Option<&MarketConfig> {
235 self.markets.get(&target_id)
236 }
237
238 #[must_use]
239 pub fn principal_entry(&self, target_id: TargetId) -> Option<u128> {
240 self.principals.get(&target_id).copied()
241 }
242
243 #[must_use]
244 pub fn cap_group(&self, cap_group_id: &CapGroupId) -> Option<&CapGroupRecord> {
245 self.cap_groups.get(cap_group_id)
246 }
247
248 pub fn replace_supply_queue(
249 &mut self,
250 supply_queue: SupplyQueue,
251 ) -> Result<(), PolicyStateError> {
252 supply_queue
253 .validate()
254 .map_err(|source| PolicyStateError::InvalidSupplyQueue { source })?;
255 self.validate_supply_queue_targets_with(&supply_queue)?;
256 self.supply_queue = supply_queue;
257 Ok(())
258 }
259
260 pub fn set_market_config(
261 &mut self,
262 target_id: TargetId,
263 config: MarketConfig,
264 ) -> Result<(), PolicyStateError> {
265 self.ensure_known_cap_group(config.cap_group_id.as_ref())?;
266 let should_purge_queue = !config.enabled || config.cap == 0;
267 self.markets.insert(target_id, config);
268 let _ = self
269 .principals
270 .insert(target_id, self.principal_entry(target_id).unwrap_or(0));
271 if should_purge_queue {
272 self.supply_queue.remove_target(target_id);
273 }
274 self.recompute_cap_group_principals()
275 }
276
277 pub fn set_market_cap(
278 &mut self,
279 target_id: TargetId,
280 cap: u128,
281 ) -> Result<(), PolicyStateError> {
282 let config = self
283 .markets
284 .get_mut(&target_id)
285 .ok_or(PolicyStateError::UnknownMarket { target_id })?;
286 config.cap = cap;
287 if cap == 0 {
288 self.supply_queue.remove_target(target_id);
289 }
290 Ok(())
291 }
292
293 pub fn set_market_enabled(
294 &mut self,
295 target_id: TargetId,
296 enabled: bool,
297 ) -> Result<(), PolicyStateError> {
298 let config = self
299 .markets
300 .get_mut(&target_id)
301 .ok_or(PolicyStateError::UnknownMarket { target_id })?;
302 config.enabled = enabled;
303 if !enabled {
304 self.supply_queue.remove_target(target_id);
305 }
306 Ok(())
307 }
308
309 pub fn set_market_cap_group(
310 &mut self,
311 target_id: TargetId,
312 cap_group_id: Option<CapGroupId>,
313 ) -> Result<(), PolicyStateError> {
314 self.ensure_known_cap_group(cap_group_id.as_ref())?;
315 let config = self
316 .markets
317 .get_mut(&target_id)
318 .ok_or(PolicyStateError::UnknownMarket { target_id })?;
319 config.cap_group_id = cap_group_id;
320 self.recompute_cap_group_principals()
321 }
322
323 pub fn set_principal(
324 &mut self,
325 target_id: TargetId,
326 principal: u128,
327 ) -> Result<(), PolicyStateError> {
328 if !self.markets.contains_key(&target_id) {
329 return Err(PolicyStateError::UnknownMarket { target_id });
330 }
331 let _ = self.principals.insert(target_id, principal);
332 self.recompute_cap_group_principals()
333 }
334
335 pub fn remove_market(
336 &mut self,
337 target_id: TargetId,
338 ) -> Result<Option<MarketConfig>, PolicyStateError> {
339 let removed = self.markets.remove(&target_id);
340 if removed.is_some() {
341 let _ = self.principals.remove(&target_id);
342 self.supply_queue.remove_target(target_id);
343 self.recompute_cap_group_principals()?;
344 }
345 Ok(removed)
346 }
347
348 pub fn remove_cap_group(
349 &mut self,
350 cap_group_id: &CapGroupId,
351 ) -> Result<Option<CapGroupRecord>, PolicyStateError> {
352 if self
353 .markets
354 .values()
355 .any(|config| config.cap_group_id.as_ref() == Some(cap_group_id))
356 {
357 return Err(PolicyStateError::CapGroupInUse {
358 id: cap_group_id.clone(),
359 });
360 }
361
362 Ok(self.cap_groups.remove(cap_group_id))
363 }
364
365 #[must_use]
366 pub fn principal_for(&self, target_id: TargetId) -> Option<u128> {
367 self.principal_entry(target_id)
368 }
369
370 pub fn external_assets(&self) -> Result<u128, PolicyStateError> {
372 self.principals
373 .iter()
374 .try_fold(0u128, |acc, (target_id, principal)| {
375 acc.checked_add(*principal)
376 .ok_or(PolicyStateError::PrincipalOverflow {
377 target_id: *target_id,
378 })
379 })
380 }
381
382 pub fn compute_cap_group_totals(&self) -> Result<Vec<(CapGroupId, u128)>, PolicyStateError> {
386 let mut totals: Vec<(CapGroupId, u128)> = Vec::new();
387
388 for (target_id, config) in self.markets.iter() {
389 let group_id = match &config.cap_group_id {
390 Some(id) => id.clone(),
391 None => continue,
392 };
393 let principal = self.principal_entry(*target_id).unwrap_or(0);
394 if let Some((_, sum)) = totals
395 .iter_mut()
396 .find(|(existing_group_id, _)| *existing_group_id == group_id)
397 {
398 *sum = sum
399 .checked_add(principal)
400 .ok_or(PolicyStateError::PrincipalOverflow {
401 target_id: *target_id,
402 })?;
403 } else {
404 totals.push((group_id, principal));
405 }
406 }
407
408 Ok(totals)
409 }
410
411 pub fn ensure_cap_group(&mut self, cap_group_id: CapGroupId) {
412 if !self.cap_groups.contains_key(&cap_group_id) {
413 let _ = self
414 .cap_groups
415 .insert(cap_group_id, CapGroupRecord::default());
416 }
417 }
418
419 pub fn set_cap_group_absolute_cap(&mut self, cap_group_id: CapGroupId, new_cap: Option<u128>) {
420 self.ensure_cap_group(cap_group_id.clone());
421 if let Some(record) = self.cap_groups.get_mut(&cap_group_id) {
422 record.cap.set_absolute_cap(new_cap);
423 }
424 }
425
426 pub fn set_cap_group_relative_cap(
427 &mut self,
428 cap_group_id: CapGroupId,
429 new_relative_cap: Option<templar_vault_kernel::Wad>,
430 ) {
431 self.ensure_cap_group(cap_group_id.clone());
432 if let Some(record) = self.cap_groups.get_mut(&cap_group_id) {
433 record.cap.set_relative_cap(new_relative_cap);
434 }
435 }
436
437 pub fn recompute_cap_group_principals(&mut self) -> Result<(), PolicyStateError> {
438 self.validate_cap_group_memberships()?;
439 let totals = self.compute_cap_group_totals()?;
440
441 for (group_id, record) in self.cap_groups.iter_mut() {
442 let total = totals
443 .iter()
444 .find(|(candidate, _)| candidate == group_id)
445 .map(|(_, sum)| *sum)
446 .unwrap_or(0);
447 record.principal = total;
448 }
449
450 Ok(())
451 }
452
453 pub fn prune_unused_cap_groups(&mut self) {
454 self.cap_groups.retain(|group_id, _| {
455 self.markets
456 .values()
457 .any(|config| config.cap_group_id.as_ref() == Some(group_id))
458 });
459 }
460
461 pub fn prune_zero_principals(&mut self) {
462 self.principals
463 .retain(|target_id, principal| *principal != 0 && self.markets.contains_key(target_id));
464 }
465
466 fn initialize_missing_principals(&mut self) {
467 let market_ids: Vec<TargetId> = self.markets.keys().copied().collect();
468 for target_id in market_ids {
469 if !self.principals.contains_key(&target_id) {
470 let _ = self.principals.insert(target_id, 0);
471 }
472 }
473 }
474
475 fn validate_supply_queue_targets(&self) -> Result<(), PolicyStateError> {
476 self.validate_supply_queue_targets_with(&self.supply_queue)
477 }
478
479 fn validate_supply_queue_targets_with(
480 &self,
481 supply_queue: &SupplyQueue,
482 ) -> Result<(), PolicyStateError> {
483 for entry in supply_queue.entries() {
484 let target_id = entry.target_id;
485 let config = self
486 .market_config(target_id)
487 .ok_or(PolicyStateError::SupplyQueueUnknownMarket { target_id })?;
488
489 if !config.enabled {
490 return Err(PolicyStateError::SupplyQueueDisabledMarket { target_id });
491 }
492
493 if config.cap == 0 {
494 return Err(PolicyStateError::SupplyQueueUnauthorizedMarket { target_id });
495 }
496 }
497
498 Ok(())
499 }
500
501 fn prune_orphan_principals(&mut self) {
502 self.principals
503 .retain(|target_id, _| self.markets.contains_key(target_id));
504 }
505
506 fn ensure_known_cap_group(
507 &self,
508 cap_group_id: Option<&CapGroupId>,
509 ) -> Result<(), PolicyStateError> {
510 match cap_group_id {
511 Some(id) if !self.cap_groups.contains_key(id) => {
512 Err(PolicyStateError::UnknownCapGroup { id: id.clone() })
513 }
514 _ => Ok(()),
515 }
516 }
517
518 fn validate_cap_group_memberships(&self) -> Result<(), PolicyStateError> {
519 for (_, config) in self.markets.iter() {
520 self.ensure_known_cap_group(config.cap_group_id.as_ref())?;
521 }
522
523 Ok(())
524 }
525
526 #[must_use]
527 pub fn is_empty(&self) -> bool {
528 self.leases.is_empty()
529 && self.markets.is_empty()
530 && self.principals.is_empty()
531 && self.cap_groups.is_empty()
532 && self.supply_queue.is_empty()
533 }
534}
535
536impl From<PolicyStateError> for CapGroupError {
537 fn from(err: PolicyStateError) -> Self {
538 match err {
539 PolicyStateError::UnknownCapGroup { id } => Self::NotFound { id },
540 PolicyStateError::CapGroupInUse { id } => Self::InconsistentRecord { id },
541 PolicyStateError::PrincipalOverflow { target_id: _ }
542 | PolicyStateError::UnknownMarket { target_id: _ }
543 | PolicyStateError::InvalidSupplyQueue { source: _ }
544 | PolicyStateError::SupplyQueueUnknownMarket { target_id: _ }
545 | PolicyStateError::SupplyQueueDisabledMarket { target_id: _ }
546 | PolicyStateError::SupplyQueueUnauthorizedMarket { target_id: _ } => {
547 Self::InconsistentRecord {
548 id: CapGroupId::policy_state_sentinel(),
549 }
550 }
551 }
552 }
553}