NAC_Blockchain/nac-udm/src/l1_protocol/acc/acc_xtzh.rs

173 lines
8.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 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<Address, u128>,
/// 储备资产
pub reserve_assets: Vec<ReserveAsset>,
/// 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<XTZHProtocolEvent>,
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<XTZHProtocolEvent> { std::mem::take(&mut self.pending_events) }
}