NAC_Blockchain/nvm_v2/nvm-l1/src/defi/collateral_lending.rs

504 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.

// Collateral Lending System
// 资产抵押借贷系统 - RWA资产抵押借XTZH
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// 贷款状态
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LoanStatus {
Active, // 活跃
Repaid, // 已还款
Liquidated, // 已清算
Defaulted, // 违约
}
/// 贷款记录
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Loan {
/// 贷款ID
pub loan_id: String,
/// 借款人
pub borrower: String,
/// 抵押资产TOKEN ID
pub collateral_asset_id: String,
/// 抵押份额数量
pub collateral_amount: u128,
/// 抵押资产估值XTZH
pub collateral_value: u128,
/// 借款金额XTZH
pub loan_amount: u128,
/// 年利率基点如500=5%
pub interest_rate: u16,
/// 抵押率基点如15000=150%
pub ltv_ratio: u16,
/// 创建时间
pub created_at: u64,
/// 到期时间
pub due_date: u64,
/// 已还本金
pub repaid_principal: u128,
/// 已还利息
pub repaid_interest: u128,
/// 状态
pub status: LoanStatus,
}
impl Loan {
/// 计算应付利息
pub fn calculate_interest(&self, current_time: u64) -> u128 {
if current_time <= self.created_at {
return 0;
}
let time_elapsed = current_time - self.created_at;
let years = time_elapsed as u128 * 1_000_000_000_000_000_000 / (365 * 24 * 3600);
let outstanding = self.loan_amount - self.repaid_principal;
(outstanding * self.interest_rate as u128 * years) / (10000 * 1_000_000_000_000_000_000)
}
/// 计算总应还金额
pub fn calculate_total_due(&self, current_time: u64) -> u128 {
let interest = self.calculate_interest(current_time);
let outstanding_principal = self.loan_amount - self.repaid_principal;
outstanding_principal + interest - self.repaid_interest
}
/// 计算当前健康度(抵押价值/贷款价值)
pub fn calculate_health_factor(&self, current_collateral_value: u128) -> u128 {
if self.loan_amount == 0 {
return u128::MAX;
}
(current_collateral_value * 10000) / self.loan_amount
}
/// 是否需要清算
pub fn needs_liquidation(&self, current_collateral_value: u128, liquidation_threshold: u16) -> bool {
let health_factor = self.calculate_health_factor(current_collateral_value);
health_factor < liquidation_threshold as u128
}
}
/// 抵押借贷系统
pub struct CollateralLendingSystem {
/// 贷款记录 (loan_id -> Loan)
loans: HashMap<String, Loan>,
/// 用户XTZH余额
xtzh_balances: HashMap<String, u128>,
/// 用户资产份额 (user, asset_id) -> shares
asset_shares: HashMap<(String, String), u128>,
/// 资产估值 (asset_id -> value in XTZH)
asset_valuations: HashMap<String, u128>,
/// 系统参数
params: LendingParams,
/// 统计信息
stats: LendingStats,
}
/// 借贷参数
#[derive(Debug, Clone)]
pub struct LendingParams {
/// 最大抵押率基点如7500=75%
pub max_ltv: u16,
/// 清算阈值基点如12000=120%
pub liquidation_threshold: u16,
/// 清算奖励基点如500=5%
pub liquidation_bonus: u16,
/// 默认年利率(基点)
pub default_interest_rate: u16,
}
impl Default for LendingParams {
fn default() -> Self {
Self {
max_ltv: 7500, // 75%
liquidation_threshold: 12000, // 120%
liquidation_bonus: 500, // 5%
default_interest_rate: 800, // 8%
}
}
}
/// 借贷统计
#[derive(Debug, Clone, Default)]
pub struct LendingStats {
/// 总贷款数
pub total_loans: u64,
/// 活跃贷款数
pub active_loans: u64,
/// 总借出金额
pub total_lent: u128,
/// 总抵押价值
pub total_collateral_value: u128,
/// 总清算次数
pub total_liquidations: u64,
}
impl CollateralLendingSystem {
/// 创建新系统
pub fn new(params: LendingParams) -> Self {
Self {
loans: HashMap::new(),
xtzh_balances: HashMap::new(),
asset_shares: HashMap::new(),
asset_valuations: HashMap::new(),
params,
stats: LendingStats::default(),
}
}
/// 设置XTZH余额
pub fn set_xtzh_balance(&mut self, user: String, amount: u128) {
self.xtzh_balances.insert(user, amount);
}
/// 获取XTZH余额
pub fn get_xtzh_balance(&self, user: &str) -> u128 {
*self.xtzh_balances.get(user).unwrap_or(&0)
}
/// 设置资产份额
pub fn set_asset_shares(&mut self, user: String, asset_id: String, shares: u128) {
self.asset_shares.insert((user, asset_id), shares);
}
/// 获取资产份额
pub fn get_asset_shares(&self, user: &str, asset_id: &str) -> u128 {
*self.asset_shares.get(&(user.to_string(), asset_id.to_string())).unwrap_or(&0)
}
/// 设置资产估值
pub fn set_asset_valuation(&mut self, asset_id: String, value: u128) {
self.asset_valuations.insert(asset_id, value);
}
/// 获取资产估值
pub fn get_asset_valuation(&self, asset_id: &str) -> u128 {
*self.asset_valuations.get(asset_id).unwrap_or(&0)
}
/// 创建贷款
pub fn create_loan(
&mut self,
borrower: String,
collateral_asset_id: String,
collateral_amount: u128,
loan_amount: u128,
duration_days: u64,
) -> Result<String, String> {
// 验证抵押品
let asset_shares = self.get_asset_shares(&borrower, &collateral_asset_id);
if asset_shares < collateral_amount {
return Err(format!("Insufficient collateral: {} < {}", asset_shares, collateral_amount));
}
// 获取抵押品估值
let unit_value = self.get_asset_valuation(&collateral_asset_id);
if unit_value == 0 {
return Err("Asset valuation not available".to_string());
}
let collateral_value = unit_value * collateral_amount;
// 验证抵押率
let ltv = (loan_amount * 10000) / collateral_value;
if ltv > self.params.max_ltv as u128 {
return Err(format!("LTV too high: {}% > {}%", ltv / 100, self.params.max_ltv / 100));
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("mainnet: handle error")
.as_secs();
let loan_id = format!("loan_{}_{}", borrower, now);
let due_date = now + duration_days * 24 * 3600;
// 锁定抵押品
self.set_asset_shares(
borrower.clone(),
collateral_asset_id.clone(),
asset_shares - collateral_amount,
);
// 发放贷款
let xtzh_balance = self.get_xtzh_balance(&borrower);
self.set_xtzh_balance(borrower.clone(), xtzh_balance + loan_amount);
// 创建贷款记录
let loan = Loan {
loan_id: loan_id.clone(),
borrower,
collateral_asset_id,
collateral_amount,
collateral_value,
loan_amount,
interest_rate: self.params.default_interest_rate,
ltv_ratio: ltv as u16,
created_at: now,
due_date,
repaid_principal: 0,
repaid_interest: 0,
status: LoanStatus::Active,
};
self.loans.insert(loan_id.clone(), loan);
self.stats.total_loans += 1;
self.stats.active_loans += 1;
self.stats.total_lent += loan_amount;
self.stats.total_collateral_value += collateral_value;
Ok(loan_id)
}
/// 还款
pub fn repay_loan(
&mut self,
loan_id: &str,
amount: u128,
) -> Result<(), String> {
// 先收集需要的数据
let (borrower, collateral_asset_id, collateral_amount) = {
let loan = self.loans.get(loan_id).ok_or("Loan not found")?;
if loan.status != LoanStatus::Active {
return Err("Loan is not active".to_string());
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("mainnet: handle error")
.as_secs();
let total_due = loan.calculate_total_due(now);
if amount > total_due {
return Err(format!("Amount exceeds total due: {} > {}", amount, total_due));
}
// 验证余额
let xtzh_balance = self.get_xtzh_balance(&loan.borrower);
if xtzh_balance < amount {
return Err(format!("Insufficient XTZH: {} < {}", xtzh_balance, amount));
}
(loan.borrower.clone(), loan.collateral_asset_id.clone(), loan.collateral_amount)
};
// 扣除还款
let xtzh_balance = self.get_xtzh_balance(&borrower);
self.set_xtzh_balance(borrower.clone(), xtzh_balance - amount);
// 更新贷款记录
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("mainnet: handle error")
.as_secs();
let loan = self.loans.get_mut(loan_id).expect("mainnet: handle error");
let interest_due = loan.calculate_interest(now) - loan.repaid_interest;
if amount >= interest_due {
// 先还利息,再还本金
loan.repaid_interest += interest_due;
loan.repaid_principal += amount - interest_due;
} else {
// 只还利息
loan.repaid_interest += amount;
}
// 检查是否全部还清
if loan.repaid_principal >= loan.loan_amount {
loan.status = LoanStatus::Repaid;
self.stats.active_loans -= 1;
// 归还抵押品
let asset_shares = self.get_asset_shares(&borrower, &collateral_asset_id);
self.set_asset_shares(
borrower,
collateral_asset_id,
asset_shares + collateral_amount,
);
}
Ok(())
}
/// 清算贷款
pub fn liquidate_loan(
&mut self,
loan_id: &str,
liquidator: String,
) -> Result<u128, String> {
// 先收集所有需要的数据
let (collateral_asset_id, collateral_amount, liquidation_amount) = {
let loan = self.loans.get(loan_id).ok_or("Loan not found")?;
if loan.status != LoanStatus::Active {
return Err("Loan is not active".to_string());
}
// 获取当前抵押品价值
let unit_value = self.get_asset_valuation(&loan.collateral_asset_id);
let current_collateral_value = unit_value * loan.collateral_amount;
// 检查是否需要清算
if !loan.needs_liquidation(current_collateral_value, self.params.liquidation_threshold) {
return Err("Loan is healthy, cannot liquidate".to_string());
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("mainnet: handle error")
.as_secs();
let total_due = loan.calculate_total_due(now);
// 计算清算金额(含奖励)
let liquidation_amount = total_due + (total_due * self.params.liquidation_bonus as u128 / 10000);
(loan.collateral_asset_id.clone(), loan.collateral_amount, liquidation_amount)
};
// 验证清算人余额
let liquidator_balance = self.get_xtzh_balance(&liquidator);
if liquidator_balance < liquidation_amount {
return Err(format!("Insufficient XTZH for liquidation: {} < {}", liquidator_balance, liquidation_amount));
}
// 扣除清算人XTZH
self.set_xtzh_balance(liquidator.clone(), liquidator_balance - liquidation_amount);
// 转移抵押品给清算人
let liquidator_shares = self.get_asset_shares(&liquidator, &collateral_asset_id);
self.set_asset_shares(
liquidator,
collateral_asset_id,
liquidator_shares + collateral_amount,
);
// 更新贷款状态
let loan = self.loans.get_mut(loan_id).expect("mainnet: handle error");
loan.status = LoanStatus::Liquidated;
self.stats.active_loans -= 1;
self.stats.total_liquidations += 1;
Ok(liquidation_amount)
}
/// 获取贷款
pub fn get_loan(&self, loan_id: &str) -> Option<&Loan> {
self.loans.get(loan_id)
}
/// 获取统计信息
pub fn get_stats(&self) -> &LendingStats {
&self.stats
}
/// 获取参数
pub fn get_params(&self) -> &LendingParams {
&self.params
}
}
impl Default for CollateralLendingSystem {
fn default() -> Self {
Self::new(LendingParams::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_loan() {
let mut system = CollateralLendingSystem::default();
system.set_asset_shares("borrower".to_string(), "asset1".to_string(), 100);
system.set_asset_valuation("asset1".to_string(), 1_000_000_000_000_000_000_000);
let loan_id = system.create_loan(
"borrower".to_string(),
"asset1".to_string(),
10,
7_000_000_000_000_000_000_000,
365,
).expect("mainnet: handle error");
assert!(system.get_loan(&loan_id).is_some());
assert_eq!(system.stats.total_loans, 1);
assert_eq!(system.stats.active_loans, 1);
}
#[test]
fn test_repay_loan() {
let mut system = CollateralLendingSystem::default();
system.set_asset_shares("borrower".to_string(), "asset1".to_string(), 100);
system.set_asset_valuation("asset1".to_string(), 1_000_000_000_000_000_000_000);
let loan_id = system.create_loan(
"borrower".to_string(),
"asset1".to_string(),
10,
7_000_000_000_000_000_000_000,
365,
).expect("mainnet: handle error");
let loan_amount = system.get_loan(&loan_id).expect("mainnet: handle error").loan_amount;
system.repay_loan(&loan_id, loan_amount).expect("mainnet: handle error");
let loan = system.get_loan(&loan_id).expect("mainnet: handle error");
assert_eq!(loan.status, LoanStatus::Repaid);
}
#[test]
fn test_liquidation() {
let mut system = CollateralLendingSystem::default();
system.set_asset_shares("borrower".to_string(), "asset1".to_string(), 100);
system.set_asset_valuation("asset1".to_string(), 1_000_000_000_000_000_000_000);
let loan_id = system.create_loan(
"borrower".to_string(),
"asset1".to_string(),
10,
7_000_000_000_000_000_000_000,
365,
).expect("mainnet: handle error");
// 抵押品价值下跌
system.set_asset_valuation("asset1".to_string(), 500_000_000_000_000_000_000);
system.set_xtzh_balance("liquidator".to_string(), 10_000_000_000_000_000_000_000);
let result = system.liquidate_loan(&loan_id, "liquidator".to_string());
assert!(result.is_ok());
assert_eq!(system.stats.total_liquidations, 1);
}
#[test]
fn test_health_factor() {
let mut system = CollateralLendingSystem::default();
system.set_asset_shares("borrower".to_string(), "asset1".to_string(), 100);
system.set_asset_valuation("asset1".to_string(), 1_000_000_000_000_000_000_000);
let loan_id = system.create_loan(
"borrower".to_string(),
"asset1".to_string(),
10,
7_000_000_000_000_000_000_000,
365,
).expect("mainnet: handle error");
let loan = system.get_loan(&loan_id).expect("mainnet: handle error");
let health_factor = loan.calculate_health_factor(10_000_000_000_000_000_000_000);
// 健康度应该是 10000/7000 * 10000 = 14285
assert!(health_factor > 14000 && health_factor < 15000);
}
}