NAC_Blockchain/nac-sdk/src/protocols/acc20c.rs

503 lines
17 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-20C: 兼容层协议接口
//!
//! 提供与NAC区块链上ACC-20C兼容层交互的客户端接口
// NacLensClient 已更名为 NAC Lens 客户端
use crate::error::{NACError, Result};
use crate::adapters::NacLensClient;
use nac_udm::primitives::{Address, Hash, Timestamp};
use nac_udm::l1_protocol::gnacs::GNACSCode;
use nac_udm::l1_protocol::acc20c::{
WrappedAsset, WrapperConfig, WrapperStatus, WrappedAssetStatus,
ComplianceSnapshot, EthAddress, u256,
};
use serde_json::json;
/// ACC-20C兼容层接口
///
/// ACC-20C是NAC与以太坊生态之间的战略性桥梁提供
/// - 双向包装NAC ACC-20 ↔ 以太坊 ERC-721
/// - 合规保留包装后仍保留KYC/AML检查
/// - 状态同步:跨链事件监听和状态同步
/// - 元数据生成符合OpenSea标准的ERC-721元数据
pub struct ACC20C {
client: NacLensClient,
}
impl ACC20C {
/// 创建新的ACC-20C接口实例
pub fn new(client: NacLensClient) -> Self {
Self { client }
}
/// 包装ACC-20为ERC-721
///
/// 将NAC链上的ACC-20资产包装成以太坊链上的ERC-721 NFT
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
/// * `owner` - NAC链上的所有者地址
/// * `eth_recipient` - 以太坊链上的接收者地址
/// * `amount` - 包装数量
///
/// # 返回
/// ERC-721 Token ID
///
/// # 流程
/// 1. 验证包装器状态
/// 2. 验证合规性
/// 3. 锁定ACC-20资产
/// 4. 生成ERC-721 NFT
/// 5. 记录包装信息
pub async fn wrap(
&self,
wrapper_address: &Address,
owner: &Address,
eth_recipient: &EthAddress,
amount: u128,
) -> Result<u256> {
let params = json!({
"wrapper_address": wrapper_address,
"owner": owner,
"eth_recipient": format!("0x{}", hex::encode(eth_recipient.0)),
"amount": amount.to_string(),
});
let response = self.client.call("acc20c_wrap", params).await?;
let token_id_data = &response["result"]["token_id"];
let token_id = u256 {
low: token_id_data["low"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing token_id.low".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid token_id.low: {}", e)))?,
high: token_id_data["high"]
.as_str()
.unwrap_or("0")
.parse()
.unwrap_or(0),
};
Ok(token_id)
}
/// 解包装ERC-721为ACC-20
///
/// 将以太坊链上的ERC-721 NFT解包装回NAC链上的ACC-20资产
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
/// * `token_id` - ERC-721 Token ID
/// * `eth_owner` - 以太坊链上的所有者地址
/// * `nac_recipient` - NAC链上的接收者地址
///
/// # 返回
/// 解包装的ACC-20数量
///
/// # 流程
/// 1. 验证TokenId存在
/// 2. 验证所有权
/// 3. 销毁ERC-721 NFT
/// 4. 解锁ACC-20资产
/// 5. 转回接收者
pub async fn unwrap(
&self,
wrapper_address: &Address,
token_id: u256,
eth_owner: &EthAddress,
nac_recipient: &Address,
) -> Result<u128> {
let params = json!({
"wrapper_address": wrapper_address,
"token_id": {
"low": token_id.low.to_string(),
"high": token_id.high.to_string(),
},
"eth_owner": format!("0x{}", hex::encode(eth_owner.0)),
"nac_recipient": nac_recipient,
});
let response = self.client.call("acc20c_unwrap", params).await?;
let amount = response["result"]["amount"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing amount".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid amount: {}", e)))?;
Ok(amount)
}
/// 查询包装资产信息
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
/// * `token_id` - ERC-721 Token ID
///
/// # 返回
/// 包装资产详细信息
pub async fn get_wrapped_asset(
&self,
wrapper_address: &Address,
token_id: u256,
) -> Result<WrappedAsset> {
let params = json!({
"wrapper_address": wrapper_address,
"token_id": {
"low": token_id.low.to_string(),
"high": token_id.high.to_string(),
},
});
let response = self.client.call("acc20c_getWrappedAsset", params).await?;
let asset_data = &response["result"];
// 解析以太坊地址
let eth_owner_hex = asset_data["current_owner"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing current_owner".to_string()))?
.trim_start_matches("0x");
let eth_owner_bytes = hex::decode(eth_owner_hex)
.map_err(|e| NACError::InvalidAddress(format!("Invalid eth address: {}", e)))?;
let mut eth_owner_array = [0u8; 20];
eth_owner_array.copy_from_slice(&eth_owner_bytes[..20]);
let wrapped_asset = WrappedAsset {
token_id,
original_holdings: asset_data["original_holdings"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing original_holdings".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid holdings: {}", e)))?,
original_owner: Address::from_hex(
asset_data["original_owner"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing original_owner".to_string()))?
).map_err(|e| NACError::InvalidAddress(format!("Invalid original_owner: {}", e)))?,
current_owner: EthAddress(eth_owner_array),
gnacs_code: serde_json::from_value(asset_data["gnacs_code"].clone())
.map_err(|e| NACError::InvalidResponse(format!("Invalid gnacs_code: {}", e)))?,
wrapped_at: Timestamp::from_secs(
asset_data["wrapped_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing wrapped_at".to_string()))?
),
metadata_uri: asset_data["metadata_uri"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing metadata_uri".to_string()))?
.to_string(),
compliance_snapshot: self.parse_compliance_snapshot(&asset_data["compliance_snapshot"])?,
status: serde_json::from_value(asset_data["status"].clone())
.map_err(|e| NACError::InvalidResponse(format!("Invalid status: {}", e)))?,
};
Ok(wrapped_asset)
}
/// 查询锁定的持有量
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
/// * `owner` - 所有者地址
///
/// # 返回
/// 锁定的ACC-20数量
pub async fn get_locked_holdings(
&self,
wrapper_address: &Address,
owner: &Address,
) -> Result<u128> {
let params = json!({
"wrapper_address": wrapper_address,
"owner": owner,
});
let response = self.client.call("acc20c_getLockedHoldings", params).await?;
let holdings = response["result"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing holdings".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid holdings: {}", e)))?;
Ok(holdings)
}
/// 冻结包装资产
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
/// * `token_id` - ERC-721 Token ID
pub async fn freeze_wrapped_asset(
&self,
wrapper_address: &Address,
token_id: u256,
) -> Result<()> {
let params = json!({
"wrapper_address": wrapper_address,
"token_id": {
"low": token_id.low.to_string(),
"high": token_id.high.to_string(),
},
});
self.client.call("acc20c_freezeWrappedAsset", params).await?;
Ok(())
}
/// 解冻包装资产
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
/// * `token_id` - ERC-721 Token ID
pub async fn unfreeze_wrapped_asset(
&self,
wrapper_address: &Address,
token_id: u256,
) -> Result<()> {
let params = json!({
"wrapper_address": wrapper_address,
"token_id": {
"low": token_id.low.to_string(),
"high": token_id.high.to_string(),
},
});
self.client.call("acc20c_unfreezeWrappedAsset", params).await?;
Ok(())
}
/// 查询包装器配置
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
///
/// # 返回
/// 包装器配置信息
pub async fn get_wrapper_config(
&self,
wrapper_address: &Address,
) -> Result<WrapperConfig> {
let params = json!({
"wrapper_address": wrapper_address,
});
let response = self.client.call("acc20c_getWrapperConfig", params).await?;
let config_data = &response["result"];
let config = WrapperConfig {
acc20_per_nft: config_data["acc20_per_nft"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing acc20_per_nft".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid acc20_per_nft: {}", e)))?,
allow_partial_wrap: config_data["allow_partial_wrap"]
.as_bool()
.ok_or(NACError::InvalidResponse("Missing allow_partial_wrap".to_string()))?,
wrap_fee_bps: config_data["wrap_fee_bps"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing wrap_fee_bps".to_string()))? as u16,
unwrap_fee_bps: config_data["unwrap_fee_bps"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing unwrap_fee_bps".to_string()))? as u16,
fee_recipient: Address::from_hex(
config_data["fee_recipient"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing fee_recipient".to_string()))?
).map_err(|e| NACError::InvalidAddress(format!("Invalid fee_recipient: {}", e)))?,
preserve_compliance: config_data["preserve_compliance"]
.as_bool()
.ok_or(NACError::InvalidResponse("Missing preserve_compliance".to_string()))?,
min_wrap_amount: config_data["min_wrap_amount"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing min_wrap_amount".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid min_wrap_amount: {}", e)))?,
max_wrap_amount: config_data["max_wrap_amount"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing max_wrap_amount".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid max_wrap_amount: {}", e)))?,
};
Ok(config)
}
/// 查询包装器状态
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
///
/// # 返回
/// 包装器状态
pub async fn get_wrapper_status(
&self,
wrapper_address: &Address,
) -> Result<WrapperStatus> {
let params = json!({
"wrapper_address": wrapper_address,
});
let response = self.client.call("acc20c_getWrapperStatus", params).await?;
let status = serde_json::from_value(response["result"].clone())
.map_err(|e| NACError::InvalidResponse(format!("Invalid status: {}", e)))?;
Ok(status)
}
/// 获取元数据URI
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
/// * `token_id` - ERC-721 Token ID
///
/// # 返回
/// 元数据URI符合ERC-721标准
pub async fn get_metadata_uri(
&self,
wrapper_address: &Address,
token_id: u256,
) -> Result<String> {
let params = json!({
"wrapper_address": wrapper_address,
"token_id": {
"low": token_id.low.to_string(),
"high": token_id.high.to_string(),
},
});
let response = self.client.call("acc20c_getMetadataURI", params).await?;
let uri = response["result"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing metadata URI".to_string()))?
.to_string();
Ok(uri)
}
/// 计算包装手续费
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
/// * `amount` - 包装数量
///
/// # 返回
/// 手续费金额
pub async fn calculate_wrap_fee(
&self,
wrapper_address: &Address,
amount: u128,
) -> Result<u128> {
let params = json!({
"wrapper_address": wrapper_address,
"amount": amount.to_string(),
});
let response = self.client.call("acc20c_calculateWrapFee", params).await?;
let fee = response["result"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing fee".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid fee: {}", e)))?;
Ok(fee)
}
/// 计算解包装手续费
///
/// # 参数
/// * `wrapper_address` - 包装器合约地址
/// * `amount` - 解包装数量
///
/// # 返回
/// 手续费金额
pub async fn calculate_unwrap_fee(
&self,
wrapper_address: &Address,
amount: u128,
) -> Result<u128> {
let params = json!({
"wrapper_address": wrapper_address,
"amount": amount.to_string(),
});
let response = self.client.call("acc20c_calculateUnwrapFee", params).await?;
let fee = response["result"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing fee".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid fee: {}", e)))?;
Ok(fee)
}
// === 私有辅助函数 ===
/// 解析合规快照
fn parse_compliance_snapshot(
&self,
snapshot_data: &serde_json::Value,
) -> Result<ComplianceSnapshot> {
let snapshot = ComplianceSnapshot {
kyc_level: snapshot_data["kyc_level"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing kyc_level".to_string()))? as u8,
aml_status: snapshot_data["aml_status"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing aml_status".to_string()))? as u8,
compliance_level: snapshot_data["compliance_level"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing compliance_level".to_string()))? as u8,
jurisdiction: snapshot_data["jurisdiction"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing jurisdiction".to_string()))? as u8,
snapshot_at: Timestamp::from_secs(
snapshot_data["snapshot_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing snapshot_at".to_string()))?
),
snapshot_hash: Hash::from_hex(
snapshot_data["snapshot_hash"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing snapshot_hash".to_string()))?
).map_err(|e| NACError::InvalidHash(format!("Invalid snapshot_hash: {}", e)))?,
};
Ok(snapshot)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_acc20c_interface() {
// 这里只是接口测试实际需要连接到NAC节点
let client = NacLensClient::new("https://rpc.newassetchain.io");
let acc20c = ACC20C::new(client);
// 测试将在实际连接到NAC节点后进行
assert!(true);
}
#[test]
fn test_u256() {
let token_id = u256::from_u128(12345);
assert_eq!(token_id.low, 12345);
assert_eq!(token_id.high, 0);
}
#[test]
fn test_eth_address() {
let eth_addr = EthAddress([0u8; 20]);
assert_eq!(eth_addr.0.len(), 20);
}
}