NAC_Blockchain/nvm_v2/acc-protocol/src/acc_collateral.rs

535 lines
16 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-Collateral: 资产抵押协议Collateral Protocol
//
// NAC原生的资产抵押标准用于借贷和DeFi应用
// 100% NAC原生协议不是任何现有标准的实现
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// 抵押状态
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CollateralStatus {
/// 活跃
Active,
/// 已清算
Liquidated,
/// 已释放
Released,
/// 已违约
Defaulted,
}
/// 抵押率类型
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CollateralRatioType {
/// 初始抵押率Initial LTV
Initial,
/// 维持抵押率Maintenance LTV
Maintenance,
/// 清算抵押率Liquidation LTV
Liquidation,
}
/// 抵押记录
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CollateralRecord {
/// 抵押ID
pub collateral_id: String,
/// 抵押资产ID
pub asset_id: String,
/// 抵押人地址
pub borrower: String,
/// 贷款人地址
pub lender: String,
/// 抵押资产价值USD以分为单位
pub collateral_value: u128,
/// 借款金额USD以分为单位
pub loan_amount: u128,
/// 初始抵押率百分比如150表示150%
pub initial_ltv: u16,
/// 维持抵押率(百分比)
pub maintenance_ltv: u16,
/// 清算抵押率(百分比)
pub liquidation_ltv: u16,
/// 当前抵押率(百分比)
pub current_ltv: u16,
/// 利率年化基点如500表示5%
pub interest_rate: u16,
/// 抵押状态
pub status: CollateralStatus,
/// 创建时间
pub created_at: u64,
/// 到期时间
pub expires_at: u64,
/// 最后更新时间
pub updated_at: u64,
/// 清算时间
pub liquidated_at: Option<u64>,
}
/// 抵押错误类型
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CollateralError {
/// 抵押记录不存在
RecordNotFound,
/// 抵押记录已存在
RecordAlreadyExists,
/// 资产不存在
AssetNotFound,
/// 抵押率不足
InsufficientCollateral,
/// 抵押已清算
AlreadyLiquidated,
/// 抵押已释放
AlreadyReleased,
/// 未到期
NotExpired,
/// 未授权操作
Unauthorized,
/// 无效的抵押率
InvalidLTV,
}
/// 抵押状态
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CollateralState {
/// 抵押记录映射 (collateral_id -> CollateralRecord)
pub collaterals: HashMap<String, CollateralRecord>,
/// 资产抵押映射 (asset_id -> collateral_id)
pub asset_collaterals: HashMap<String, String>,
/// 借款人抵押列表 (borrower -> [collateral_ids])
pub borrower_collaterals: HashMap<String, Vec<String>>,
/// 贷款人抵押列表 (lender -> [collateral_ids])
pub lender_collaterals: HashMap<String, Vec<String>>,
}
/// ACC-Collateral接口
pub trait ACCCollateral {
/// 创建抵押
fn create_collateral(&mut self, record: CollateralRecord) -> Result<(), CollateralError>;
/// 获取抵押记录
fn get_collateral(&self, collateral_id: &str) -> Result<CollateralRecord, CollateralError>;
/// 获取资产的抵押记录
fn get_asset_collateral(&self, asset_id: &str) -> Result<CollateralRecord, CollateralError>;
/// 更新抵押资产价值
fn update_collateral_value(
&mut self,
collateral_id: &str,
new_value: u128,
) -> Result<(), CollateralError>;
/// 计算当前抵押率
fn calculate_ltv(&self, collateral_id: &str) -> Result<u16, CollateralError>;
/// 检查是否需要清算
fn is_liquidatable(&self, collateral_id: &str) -> bool;
/// 清算抵押
fn liquidate(&mut self, collateral_id: &str) -> Result<(), CollateralError>;
/// 释放抵押
fn release(&mut self, collateral_id: &str) -> Result<(), CollateralError>;
/// 部分还款
fn partial_repay(
&mut self,
collateral_id: &str,
amount: u128,
) -> Result<(), CollateralError>;
/// 获取借款人的抵押列表
fn get_borrower_collaterals(&self, borrower: &str) -> Vec<CollateralRecord>;
/// 获取贷款人的抵押列表
fn get_lender_collaterals(&self, lender: &str) -> Vec<CollateralRecord>;
/// 获取所有需要清算的抵押
fn get_liquidatable_collaterals(&self) -> Vec<CollateralRecord>;
}
/// ACC-Collateral标准实现
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CollateralToken {
state: CollateralState,
}
impl CollateralToken {
/// 创建新的抵押管理器
pub fn new() -> Self {
Self {
state: CollateralState {
collaterals: HashMap::new(),
asset_collaterals: HashMap::new(),
borrower_collaterals: HashMap::new(),
lender_collaterals: HashMap::new(),
},
}
}
/// 获取状态的可变引用
pub fn state_mut(&mut self) -> &mut CollateralState {
&mut self.state
}
/// 获取状态的不可变引用
pub fn state(&self) -> &CollateralState {
&self.state
}
/// 验证抵押率
fn validate_ltv(&self, record: &CollateralRecord) -> Result<(), CollateralError> {
// LTV应该是递增的initial < maintenance < liquidation
if record.initial_ltv > record.maintenance_ltv
|| record.maintenance_ltv > record.liquidation_ltv
{
return Err(CollateralError::InvalidLTV);
}
// 检查初始抵押率是否满足要求
let ltv = (record.loan_amount * 10000 / record.collateral_value) as u16;
if ltv > record.initial_ltv {
return Err(CollateralError::InsufficientCollateral);
}
Ok(())
}
}
impl Default for CollateralToken {
fn default() -> Self {
Self::new()
}
}
impl ACCCollateral for CollateralToken {
fn create_collateral(&mut self, mut record: CollateralRecord) -> Result<(), CollateralError> {
// 检查抵押记录是否已存在
if self.state.collaterals.contains_key(&record.collateral_id) {
return Err(CollateralError::RecordAlreadyExists);
}
// 检查资产是否已被抵押
if self.state.asset_collaterals.contains_key(&record.asset_id) {
return Err(CollateralError::RecordAlreadyExists);
}
// 验证抵押率
self.validate_ltv(&record)?;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
record.created_at = now;
record.updated_at = now;
record.status = CollateralStatus::Active;
record.current_ltv = (record.loan_amount * 10000 / record.collateral_value) as u16;
let collateral_id = record.collateral_id.clone();
let asset_id = record.asset_id.clone();
let borrower = record.borrower.clone();
let lender = record.lender.clone();
// 保存抵押记录
self.state
.collaterals
.insert(collateral_id.clone(), record);
// 更新资产抵押映射
self.state
.asset_collaterals
.insert(asset_id, collateral_id.clone());
// 更新借款人抵押列表
self.state
.borrower_collaterals
.entry(borrower)
.or_insert_with(Vec::new)
.push(collateral_id.clone());
// 更新贷款人抵押列表
self.state
.lender_collaterals
.entry(lender)
.or_insert_with(Vec::new)
.push(collateral_id);
Ok(())
}
fn get_collateral(&self, collateral_id: &str) -> Result<CollateralRecord, CollateralError> {
self.state
.collaterals
.get(collateral_id)
.cloned()
.ok_or(CollateralError::RecordNotFound)
}
fn get_asset_collateral(&self, asset_id: &str) -> Result<CollateralRecord, CollateralError> {
let collateral_id = self
.state
.asset_collaterals
.get(asset_id)
.ok_or(CollateralError::AssetNotFound)?;
self.get_collateral(collateral_id)
}
fn update_collateral_value(
&mut self,
collateral_id: &str,
new_value: u128,
) -> Result<(), CollateralError> {
let mut record = self.get_collateral(collateral_id)?;
if record.status != CollateralStatus::Active {
return Err(CollateralError::AlreadyLiquidated);
}
record.collateral_value = new_value;
record.current_ltv = (record.loan_amount * 10000 / new_value) as u16;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
record.updated_at = now;
self.state
.collaterals
.insert(collateral_id.to_string(), record);
Ok(())
}
fn calculate_ltv(&self, collateral_id: &str) -> Result<u16, CollateralError> {
let record = self.get_collateral(collateral_id)?;
Ok((record.loan_amount * 10000 / record.collateral_value) as u16)
}
fn is_liquidatable(&self, collateral_id: &str) -> bool {
if let Ok(record) = self.get_collateral(collateral_id) {
record.status == CollateralStatus::Active
&& record.current_ltv >= record.liquidation_ltv
} else {
false
}
}
fn liquidate(&mut self, collateral_id: &str) -> Result<(), CollateralError> {
let mut record = self.get_collateral(collateral_id)?;
if record.status != CollateralStatus::Active {
return Err(CollateralError::AlreadyLiquidated);
}
if !self.is_liquidatable(collateral_id) {
return Err(CollateralError::InsufficientCollateral);
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
record.status = CollateralStatus::Liquidated;
record.liquidated_at = Some(now);
record.updated_at = now;
self.state
.collaterals
.insert(collateral_id.to_string(), record);
Ok(())
}
fn release(&mut self, collateral_id: &str) -> Result<(), CollateralError> {
let mut record = self.get_collateral(collateral_id)?;
if record.status != CollateralStatus::Active {
return Err(CollateralError::AlreadyReleased);
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
record.status = CollateralStatus::Released;
record.updated_at = now;
self.state
.collaterals
.insert(collateral_id.to_string(), record);
Ok(())
}
fn partial_repay(
&mut self,
collateral_id: &str,
amount: u128,
) -> Result<(), CollateralError> {
let mut record = self.get_collateral(collateral_id)?;
if record.status != CollateralStatus::Active {
return Err(CollateralError::AlreadyLiquidated);
}
if amount > record.loan_amount {
return Err(CollateralError::Unauthorized);
}
record.loan_amount -= amount;
record.current_ltv = if record.loan_amount > 0 {
(record.loan_amount * 10000 / record.collateral_value) as u16
} else {
0
};
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
record.updated_at = now;
self.state
.collaterals
.insert(collateral_id.to_string(), record);
Ok(())
}
fn get_borrower_collaterals(&self, borrower: &str) -> Vec<CollateralRecord> {
self.state
.borrower_collaterals
.get(borrower)
.map(|collateral_ids| {
collateral_ids
.iter()
.filter_map(|id| self.state.collaterals.get(id).cloned())
.collect()
})
.unwrap_or_default()
}
fn get_lender_collaterals(&self, lender: &str) -> Vec<CollateralRecord> {
self.state
.lender_collaterals
.get(lender)
.map(|collateral_ids| {
collateral_ids
.iter()
.filter_map(|id| self.state.collaterals.get(id).cloned())
.collect()
})
.unwrap_or_default()
}
fn get_liquidatable_collaterals(&self) -> Vec<CollateralRecord> {
self.state
.collaterals
.values()
.filter(|record| {
record.status == CollateralStatus::Active
&& record.current_ltv >= record.liquidation_ltv
})
.cloned()
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_record() -> CollateralRecord {
CollateralRecord {
collateral_id: "COLL-001".to_string(),
asset_id: "RWA-001".to_string(),
borrower: "alice".to_string(),
lender: "bob".to_string(),
collateral_value: 1000000_00, // $10,000
loan_amount: 600000_00, // $6,000
initial_ltv: 7000, // 70%
maintenance_ltv: 8000, // 80%
liquidation_ltv: 8500, // 85%
current_ltv: 6000, // 60%
interest_rate: 500, // 5%
status: CollateralStatus::Active,
created_at: 0,
expires_at: 1234567890 + 31536000,
updated_at: 0,
liquidated_at: None,
}
}
#[test]
fn test_create_collateral() {
let mut collateral = CollateralToken::new();
let record = create_test_record();
assert!(collateral.create_collateral(record).is_ok());
}
#[test]
fn test_calculate_ltv() {
let mut collateral = CollateralToken::new();
let record = create_test_record();
collateral.create_collateral(record).unwrap();
let ltv = collateral.calculate_ltv("COLL-001").unwrap();
assert_eq!(ltv, 6000); // 60%
}
#[test]
fn test_update_collateral_value() {
let mut collateral = CollateralToken::new();
let record = create_test_record();
collateral.create_collateral(record).unwrap();
assert!(collateral
.update_collateral_value("COLL-001", 500000_00)
.is_ok());
let ltv = collateral.calculate_ltv("COLL-001").unwrap();
assert_eq!(ltv, 12000); // 120%
}
#[test]
fn test_liquidate() {
let mut collateral = CollateralToken::new();
let record = create_test_record();
// 创建正常的抵押记录LTV = 60%
collateral.create_collateral(record).unwrap();
// 通过降低抵押品价值使LTV上升到清算阈值以上
// 原始: loan_amount=600000_00, collateral_value=1000000_00, LTV=60%
// 要达到90% LTV需要: collateral_value = 600000_00 * 10000 / 9000 = 666666_67
assert!(collateral
.update_collateral_value("COLL-001", 666666_67)
.is_ok());
// 现在LTV应该是90%超过liquidation_ltv (85%)
assert!(collateral.liquidate("COLL-001").is_ok());
assert_eq!(
collateral.get_collateral("COLL-001").unwrap().status,
CollateralStatus::Liquidated
);
}
#[test]
fn test_partial_repay() {
let mut collateral = CollateralToken::new();
let record = create_test_record();
collateral.create_collateral(record).unwrap();
assert!(collateral.partial_repay("COLL-001", 100000_00).is_ok());
assert_eq!(
collateral.get_collateral("COLL-001").unwrap().loan_amount,
500000_00
);
}
}