//! ACC-721: 唯一资产证书协议接口
//!
//! 提供与NAC区块链上ACC-721证书交互的客户端接口
use crate::client::NRPC3Client;
use crate::error::{NACError, Result};
use crate::types::*;
use nac_udm::primitives::{Address, Hash, Timestamp};
use nac_udm::l1_protocol::gnacs::GNACSCode;
use nac_udm::l1_protocol::acc::acc721::{
AssetId, SovereigntyType, AssetDNA, AssetValuation,
CustodyInfo, InsuranceInfo, CollateralInfo, FragmentationPool,
};
use serde_json::json;
/// ACC-721唯一资产证书接口
pub struct ACC721 {
client: NRPC3Client,
}
impl ACC721 {
/// 创建新的ACC-721接口实例
pub fn new(client: NRPC3Client) -> Self {
Self { client }
}
/// 获取资产持有者
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `asset_id` - 资产ID
///
/// # 返回
/// 资产持有者地址
pub async fn get_asset_holder(
&self,
certificate_address: &Address,
asset_id: AssetId,
) -> Result
{
let params = json!({
"certificate_address": certificate_address,
"asset_id": asset_id,
});
let response = self.client.call("acc721_getAssetHolder", params).await?;
let holder_hex = response["result"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing holder address".to_string()))?;
Address::from_hex(holder_hex)
.map_err(|e| NACError::InvalidAddress(format!("Invalid holder address: {}", e)))
}
/// 铸造唯一资产
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `to` - 接收者地址
/// * `asset_id` - 资产ID
/// * `metadata_uri` - 资产元数据URI
/// * `physical_fingerprint` - 物理指纹哈希
/// * `legal_document_hash` - 法律文件哈希
/// * `custodian` - 托管方地址
/// * `insurer` - 保险方地址
/// * `insurance_coverage` - 保险金额(XTZH)
/// * `insurance_expiry` - 保险到期时间
///
/// # 返回
/// 资产DNA
pub async fn mint_asset(
&self,
certificate_address: &Address,
to: &Address,
asset_id: AssetId,
metadata_uri: String,
physical_fingerprint: Hash,
legal_document_hash: Hash,
custodian: Address,
insurer: Address,
insurance_coverage: u128,
insurance_expiry: Timestamp,
) -> Result {
let params = json!({
"certificate_address": certificate_address,
"to": to,
"asset_id": asset_id,
"metadata_uri": metadata_uri,
"physical_fingerprint": physical_fingerprint,
"legal_document_hash": legal_document_hash,
"custodian": custodian,
"insurer": insurer,
"insurance_coverage": insurance_coverage,
"insurance_expiry": insurance_expiry,
});
let response = self.client.call("acc721_mintAsset", params).await?;
// 解析AssetDNA
let dna_data = &response["result"];
let asset_dna = AssetDNA {
dna_hash: Hash::from_hex(
dna_data["dna_hash"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing dna_hash".to_string()))?
)?,
physical_fingerprint,
legal_document_hash,
generated_at: Timestamp::from_secs(
dna_data["generated_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing generated_at".to_string()))?
),
};
Ok(asset_dna)
}
/// 转移唯一资产
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `from` - 发送者地址
/// * `to` - 接收者地址
/// * `asset_id` - 资产ID
///
/// # 返回
/// 宪法收据哈希
pub async fn transfer_asset(
&self,
certificate_address: &Address,
from: &Address,
to: &Address,
asset_id: AssetId,
) -> Result {
let params = json!({
"certificate_address": certificate_address,
"from": from,
"to": to,
"asset_id": asset_id,
});
let response = self.client.call("acc721_transferAsset", params).await?;
let receipt_hash = response["result"]["constitutional_receipt"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing constitutional_receipt".to_string()))?;
Hash::from_hex(receipt_hash)
.map_err(|e| NACError::InvalidHash(format!("Invalid receipt hash: {}", e)))
}
/// 授权资产
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `owner` - 资产持有者地址
/// * `approved` - 被授权者地址
/// * `asset_id` - 资产ID
pub async fn approve_asset(
&self,
certificate_address: &Address,
owner: &Address,
approved: &Address,
asset_id: AssetId,
) -> Result<()> {
let params = json!({
"certificate_address": certificate_address,
"owner": owner,
"approved": approved,
"asset_id": asset_id,
});
self.client.call("acc721_approveAsset", params).await?;
Ok(())
}
/// 销毁资产
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `owner` - 资产持有者地址
/// * `asset_id` - 资产ID
pub async fn burn_asset(
&self,
certificate_address: &Address,
owner: &Address,
asset_id: AssetId,
) -> Result<()> {
let params = json!({
"certificate_address": certificate_address,
"owner": owner,
"asset_id": asset_id,
});
self.client.call("acc721_burnAsset", params).await?;
Ok(())
}
/// 碎片化资产
///
/// 将唯一资产碎片化为多个ACC-20代币
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `owner` - 资产持有者地址
/// * `asset_id` - 资产ID
/// * `fragment_count` - 碎片总数
/// * `fragment_price` - 碎片价格(XTZH)
///
/// # 返回
/// 碎片化池信息
pub async fn fragmentize_asset(
&self,
certificate_address: &Address,
owner: &Address,
asset_id: AssetId,
fragment_count: u64,
fragment_price: u128,
) -> Result {
let params = json!({
"certificate_address": certificate_address,
"owner": owner,
"asset_id": asset_id,
"fragment_count": fragment_count,
"fragment_price": fragment_price,
});
let response = self.client.call("acc721_fragmentizeAsset", params).await?;
// 解析FragmentationPool
let pool_data = &response["result"];
let pool = FragmentationPool {
fragment_token_address: Address::from_hex(
pool_data["fragment_token_address"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing fragment_token_address".to_string()))?
)?,
total_fragments: pool_data["total_fragments"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing total_fragments".to_string()))?,
fragment_price_xtzh: pool_data["fragment_price_xtzh"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing fragment_price_xtzh".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid fragment_price: {}", e)))?,
fragmentized_at: Timestamp::from_secs(
pool_data["fragmentized_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing fragmentized_at".to_string()))?
),
is_recomposable: pool_data["is_recomposable"]
.as_bool()
.ok_or(NACError::InvalidResponse("Missing is_recomposable".to_string()))?,
};
Ok(pool)
}
/// 更新资产估值
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `asset_id` - 资产ID
/// * `valuation` - 新的资产估值
pub async fn update_valuation(
&self,
certificate_address: &Address,
asset_id: AssetId,
valuation: AssetValuation,
) -> Result<()> {
let params = json!({
"certificate_address": certificate_address,
"asset_id": asset_id,
"valuation": {
"value_xtzh": valuation.value_xtzh.to_string(),
"valuation_provider": valuation.valuation_provider,
"valued_at": valuation.valued_at.as_secs(),
"validity_period": valuation.validity_period,
},
});
self.client.call("acc721_updateValuation", params).await?;
Ok(())
}
/// 获取资产估值
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `asset_id` - 资产ID
///
/// # 返回
/// 资产估值
pub async fn get_asset_valuation(
&self,
certificate_address: &Address,
asset_id: AssetId,
) -> Result {
let params = json!({
"certificate_address": certificate_address,
"asset_id": asset_id,
});
let response = self.client.call("acc721_getAssetValuation", params).await?;
// 解析AssetValuation
let val_data = &response["result"];
let valuation = AssetValuation {
value_xtzh: val_data["value_xtzh"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing value_xtzh".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid value_xtzh: {}", e)))?,
valuation_provider: Address::from_hex(
val_data["valuation_provider"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing valuation_provider".to_string()))?
)?,
valued_at: Timestamp::from_secs(
val_data["valued_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing valued_at".to_string()))?
),
validity_period: val_data["validity_period"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing validity_period".to_string()))?,
};
Ok(valuation)
}
/// 获取资产DNA
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `asset_id` - 资产ID
///
/// # 返回
/// 资产DNA
pub async fn get_asset_dna(
&self,
certificate_address: &Address,
asset_id: AssetId,
) -> Result {
let params = json!({
"certificate_address": certificate_address,
"asset_id": asset_id,
});
let response = self.client.call("acc721_getAssetDNA", params).await?;
// 解析AssetDNA
let dna_data = &response["result"];
let asset_dna = AssetDNA {
dna_hash: Hash::from_hex(
dna_data["dna_hash"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing dna_hash".to_string()))?
)?,
physical_fingerprint: Hash::from_hex(
dna_data["physical_fingerprint"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing physical_fingerprint".to_string()))?
)?,
legal_document_hash: Hash::from_hex(
dna_data["legal_document_hash"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing legal_document_hash".to_string()))?
)?,
generated_at: Timestamp::from_secs(
dna_data["generated_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing generated_at".to_string()))?
),
};
Ok(asset_dna)
}
/// 获取资产元数据
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `asset_id` - 资产ID
///
/// # 返回
/// 资产元数据URI
pub async fn get_asset_metadata(
&self,
certificate_address: &Address,
asset_id: AssetId,
) -> Result {
let params = json!({
"certificate_address": certificate_address,
"asset_id": asset_id,
});
let response = self.client.call("acc721_getAssetMetadata", params).await?;
let metadata_uri = response["result"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing metadata URI".to_string()))?
.to_string();
Ok(metadata_uri)
}
/// 获取托管信息
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `asset_id` - 资产ID
///
/// # 返回
/// 托管信息
pub async fn get_custody_info(
&self,
certificate_address: &Address,
asset_id: AssetId,
) -> Result {
let params = json!({
"certificate_address": certificate_address,
"asset_id": asset_id,
});
let response = self.client.call("acc721_getCustodyInfo", params).await?;
// 解析CustodyInfo
let custody_data = &response["result"];
let custody_info = CustodyInfo {
custodian: Address::from_hex(
custody_data["custodian"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing custodian".to_string()))?
)?,
custody_start: Timestamp::from_secs(
custody_data["custody_start"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing custody_start".to_string()))?
),
is_active: custody_data["is_active"]
.as_bool()
.ok_or(NACError::InvalidResponse("Missing is_active".to_string()))?,
custody_proof: Hash::from_hex(
custody_data["custody_proof"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing custody_proof".to_string()))?
)?,
};
Ok(custody_info)
}
/// 获取保险信息
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `asset_id` - 资产ID
///
/// # 返回
/// 保险信息
pub async fn get_insurance_info(
&self,
certificate_address: &Address,
asset_id: AssetId,
) -> Result {
let params = json!({
"certificate_address": certificate_address,
"asset_id": asset_id,
});
let response = self.client.call("acc721_getInsuranceInfo", params).await?;
// 解析InsuranceInfo
let insurance_data = &response["result"];
let insurance_info = InsuranceInfo {
insurer: Address::from_hex(
insurance_data["insurer"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing insurer".to_string()))?
)?,
coverage_xtzh: insurance_data["coverage_xtzh"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing coverage_xtzh".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid coverage_xtzh: {}", e)))?,
insurance_start: Timestamp::from_secs(
insurance_data["insurance_start"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing insurance_start".to_string()))?
),
insurance_expiry: Timestamp::from_secs(
insurance_data["insurance_expiry"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing insurance_expiry".to_string()))?
),
policy_number: insurance_data["policy_number"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing policy_number".to_string()))?
.to_string(),
};
Ok(insurance_info)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_acc721_interface() {
// 这里只是接口测试,实际需要连接到NAC节点
let client = NRPC3Client::new("https://rpc.newassetchain.io");
let acc721 = ACC721::new(client);
// 测试将在实际连接到NAC节点后进行
assert!(true);
}
}