//! acc_xtzh - NAC 原生协议实现 //! 从 acc_remaining_protocols.rs 提取 use crate::primitives::{Address, Hash, Timestamp}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ACCXTZHError { InsufficientBalance { holder: Address, available: u128, requested: u128 }, InsufficientReserve { required: u128, available: u128 }, InvalidConstitutionalReceipt, Unauthorized(Address), SDRPegViolation { current_rate: u128, min_rate: u128, max_rate: u128 }, GoldReserveInsufficient { required_ratio: u8, actual_ratio: u8 }, } impl std::fmt::Display for ACCXTZHError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::InsufficientBalance { holder, available, requested } => write!(f, "余额不足 {}: 可用 {},请求 {}", holder.to_hex(), available, requested), Self::InsufficientReserve { required, available } => write!(f, "储备不足: 需要 {},可用 {}", required, available), Self::InvalidConstitutionalReceipt => write!(f, "宪法收据无效"), Self::Unauthorized(a) => write!(f, "未授权: {}", a.to_hex()), Self::SDRPegViolation { current_rate, min_rate, max_rate } => write!(f, "SDR 锚定偏离: 当前 {},允许范围 [{}, {}]", current_rate, min_rate, max_rate), Self::GoldReserveInsufficient { required_ratio, actual_ratio } => write!(f, "黄金储备不足: 要求 {}%,实际 {}%", required_ratio, actual_ratio), } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum ReserveAssetType { Gold, USD, EUR, GBP, JPY, CNY, NACNative } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ReserveAsset { pub asset_type: ReserveAssetType, pub amount: u128, /// 权重(基点,10000=100%) pub weight_bps: u16, pub last_updated: Timestamp, } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum XTZHProtocolEvent { Minted { recipient: Address, amount: u128, constitutional_receipt: Hash, timestamp: Timestamp }, Burned { holder: Address, amount: u128, constitutional_receipt: Hash, timestamp: Timestamp }, Transferred { from: Address, to: Address, amount: u128, timestamp: Timestamp }, ReserveRebalanced { old_gold_ratio: u8, new_gold_ratio: u8, timestamp: Timestamp }, SDRRateUpdated { old_rate: u128, new_rate: u128, timestamp: Timestamp }, } /// XTZH 稳定币协议 /// UID: nac.acc.XTZHStablecoinProtocol.v1 /// SDR 锚定 + 黄金储备保障 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct XTZHStablecoinProtocol { pub protocol_uid: String, pub lens_protocol_vector: String, pub total_supply: u128, /// 持仓(address -> 余额,精度18位) pub holdings: HashMap, /// 储备资产 pub reserve_assets: Vec, /// SDR 锚定汇率(XTZH/SDR,精度18位,1 XTZH = 1 SDR) pub sdr_peg_rate: u128, /// SDR 汇率允许偏差(基点,默认200=2%) pub sdr_tolerance_bps: u16, /// 黄金储备最低比例(百分比,默认40) pub min_gold_reserve_ratio: u8, /// 当前黄金储备比例 pub current_gold_reserve_ratio: u8, pub pending_events: Vec, pub created_at: Timestamp, pub updated_at: Timestamp, } impl XTZHStablecoinProtocol { pub fn new(sdr_peg_rate: u128, min_gold_reserve_ratio: u8) -> Self { Self { protocol_uid: "nac.acc.XTZHStablecoinProtocol.v1".to_string(), lens_protocol_vector: "ACC-XTZH".to_string(), total_supply: 0, holdings: HashMap::new(), reserve_assets: Vec::new(), sdr_peg_rate, sdr_tolerance_bps: 200, min_gold_reserve_ratio, current_gold_reserve_ratio: 0, pending_events: Vec::new(), created_at: Timestamp::now(), updated_at: Timestamp::now(), } } pub fn mint( &mut self, recipient: Address, amount: u128, constitutional_receipt: Hash, timestamp: Timestamp, ) -> Result<(), ACCXTZHError> { if constitutional_receipt.is_zero() { return Err(ACCXTZHError::InvalidConstitutionalReceipt); } if self.current_gold_reserve_ratio < self.min_gold_reserve_ratio { return Err(ACCXTZHError::GoldReserveInsufficient { required_ratio: self.min_gold_reserve_ratio, actual_ratio: self.current_gold_reserve_ratio, }); } *self.holdings.entry(recipient.clone()).or_insert(0) += amount; self.total_supply = self.total_supply.saturating_add(amount); self.pending_events.push(XTZHProtocolEvent::Minted { recipient, amount, constitutional_receipt, timestamp }); self.updated_at = Timestamp::now(); Ok(()) } pub fn burn( &mut self, holder: Address, amount: u128, constitutional_receipt: Hash, timestamp: Timestamp, ) -> Result<(), ACCXTZHError> { if constitutional_receipt.is_zero() { return Err(ACCXTZHError::InvalidConstitutionalReceipt); } let balance = self.holdings.get(&holder).copied().unwrap_or(0); if balance < amount { return Err(ACCXTZHError::InsufficientBalance { holder: holder.clone(), available: balance, requested: amount }); } *self.holdings.get_mut(&holder).unwrap() -= amount; self.total_supply = self.total_supply.saturating_sub(amount); self.pending_events.push(XTZHProtocolEvent::Burned { holder, amount, constitutional_receipt, timestamp }); self.updated_at = Timestamp::now(); Ok(()) } pub fn transfer( &mut self, from: Address, to: Address, amount: u128, timestamp: Timestamp, ) -> Result<(), ACCXTZHError> { let balance = self.holdings.get(&from).copied().unwrap_or(0); if balance < amount { return Err(ACCXTZHError::InsufficientBalance { holder: from.clone(), available: balance, requested: amount }); } *self.holdings.get_mut(&from).unwrap() -= amount; *self.holdings.entry(to.clone()).or_insert(0) += amount; self.pending_events.push(XTZHProtocolEvent::Transferred { from, to, amount, timestamp }); Ok(()) } pub fn update_sdr_rate( &mut self, new_rate: u128, constitutional_receipt: Hash, timestamp: Timestamp, ) -> Result<(), ACCXTZHError> { if constitutional_receipt.is_zero() { return Err(ACCXTZHError::InvalidConstitutionalReceipt); } let tolerance = self.sdr_peg_rate * self.sdr_tolerance_bps as u128 / 10000; let min_rate = self.sdr_peg_rate.saturating_sub(tolerance); let max_rate = self.sdr_peg_rate.saturating_add(tolerance); if new_rate < min_rate || new_rate > max_rate { return Err(ACCXTZHError::SDRPegViolation { current_rate: new_rate, min_rate, max_rate }); } let old_rate = self.sdr_peg_rate; self.sdr_peg_rate = new_rate; self.pending_events.push(XTZHProtocolEvent::SDRRateUpdated { old_rate, new_rate, timestamp }); Ok(()) } pub fn update_reserve( &mut self, asset_type: ReserveAssetType, amount: u128, weight_bps: u16, timestamp: Timestamp, ) { if let Some(r) = self.reserve_assets.iter_mut().find(|r| r.asset_type == asset_type) { r.amount = amount; r.weight_bps = weight_bps; r.last_updated = timestamp; } else { self.reserve_assets.push(ReserveAsset { asset_type, amount, weight_bps, last_updated: timestamp }); } self.recalculate_gold_ratio(); } fn recalculate_gold_ratio(&mut self) { let total_weight: u16 = self.reserve_assets.iter().map(|r| r.weight_bps).sum(); if total_weight == 0 { self.current_gold_reserve_ratio = 0; return; } let gold_weight: u16 = self.reserve_assets.iter() .filter(|r| r.asset_type == ReserveAssetType::Gold) .map(|r| r.weight_bps).sum(); self.current_gold_reserve_ratio = (gold_weight as u32 * 100 / total_weight as u32) as u8; } pub fn balance_of(&self, address: &Address) -> u128 { self.holdings.get(address).copied().unwrap_or(0) } pub fn drain_pending_events(&mut self) -> Vec { std::mem::take(&mut self.pending_events) } }