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

652 lines
22 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-1155: 多代币证书协议接口
//!
//! 提供与NAC区块链上ACC-1155证书交互的客户端接口
// NRPC3Client 已更名为 NAC Lens 客户端
use crate::error::{NACError, Result};
use crate::adapters::NRPC3Client;
use nac_udm::primitives::{Address, Hash, Timestamp};
use nac_udm::l1_protocol::gnacs::GNACSCode;
use nac_udm::l1_protocol::acc::acc1155::{
TokenId, TokenType, TokenTypeDNA, TokenTypeMetadata,
BatchTransfer, BatchMint, BatchBurn, TokenCustodyInfo, TokenInsuranceInfo,
TokenTypeValuation,
};
use nac_udm::l2_governance::sovereignty::SovereigntyRight;
use serde_json::json;
/// ACC-1155多代币证书接口
pub struct ACC1155 {
client: NRPC3Client,
}
impl ACC1155 {
/// 创建新的ACC-1155接口实例
pub fn new(client: NRPC3Client) -> Self {
Self { client }
}
/// 获取代币余额
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `holder` - 持有者地址
/// * `token_id` - 代币类型ID
///
/// # 返回
/// 代币余额
pub async fn balance_of(
&self,
certificate_address: &Address,
holder: &Address,
token_id: TokenId,
) -> Result<u128> {
let params = json!({
"certificate_address": certificate_address,
"holder": holder,
"token_id": token_id,
});
let response = self.client.call("acc1155_balanceOf", params).await?;
let balance = response["result"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing balance".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid balance: {}", e)))?;
Ok(balance)
}
/// 批量获取代币余额
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `holders` - 持有者地址列表
/// * `token_ids` - 代币类型ID列表
///
/// # 返回
/// 代币余额列表
pub async fn balance_of_batch(
&self,
certificate_address: &Address,
holders: Vec<Address>,
token_ids: Vec<TokenId>,
) -> Result<Vec<u128>> {
let params = json!({
"certificate_address": certificate_address,
"holders": holders,
"token_ids": token_ids,
});
let response = self.client.call("acc1155_balanceOfBatch", params).await?;
let balances: Vec<u128> = response["result"]
.as_array()
.ok_or(NACError::InvalidResponse("Missing balances array".to_string()))?
.iter()
.map(|v| {
v.as_str()
.ok_or(NACError::InvalidResponse("Invalid balance".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid balance: {}", e)))
})
.collect::<Result<Vec<u128>>>()?;
Ok(balances)
}
/// 创建新的代币类型
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `token_id` - 代币类型ID
/// * `token_type` - 代币类型(可替代性)
/// * `gnacs_code` - GNACS编码
/// * `sovereignty_type` - 主权类型
/// * `metadata` - 代币元数据
/// * `max_supply` - 最大供应量None表示无限制
///
/// # 返回
/// 代币类型DNA
pub async fn create_token_type(
&self,
certificate_address: &Address,
token_id: TokenId,
token_type: TokenType,
gnacs_code: GNACSCode,
sovereignty_type: SovereigntyRight,
metadata: TokenTypeMetadata,
max_supply: Option<u128>,
) -> Result<TokenTypeDNA> {
let params = json!({
"certificate_address": certificate_address,
"token_id": token_id,
"token_type": token_type,
"gnacs_code": gnacs_code,
"sovereignty_type": sovereignty_type,
"metadata": {
"name": metadata.name,
"symbol": metadata.symbol,
"uri": metadata.uri,
},
"max_supply": max_supply,
});
let response = self.client.call("acc1155_createTokenType", params).await?;
// 解析TokenTypeDNA
let dna_data = &response["result"];
let token_dna = TokenTypeDNA {
dna_hash: Hash::from_hex(
dna_data["dna_hash"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing dna_hash".to_string()))?
).map_err(|e| NACError::InvalidHash(format!("Invalid dna_hash: {}", e)))?,
token_id,
gnacs_code,
token_type,
sovereignty_type,
metadata_hash: Hash::from_hex(
dna_data["metadata_hash"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing metadata_hash".to_string()))?
).map_err(|e| NACError::InvalidHash(format!("Invalid metadata_hash: {}", e)))?,
generated_at: Timestamp::from_secs(
dna_data["generated_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing generated_at".to_string()))?
),
};
Ok(token_dna)
}
/// 批量铸造代币
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `to` - 接收者地址
/// * `token_ids` - 代币类型ID列表
/// * `amounts` - 数量列表
///
/// # 返回
/// 批量铸造记录
pub async fn mint_batch(
&self,
certificate_address: &Address,
to: &Address,
token_ids: Vec<TokenId>,
amounts: Vec<u128>,
) -> Result<BatchMint> {
let params = json!({
"certificate_address": certificate_address,
"to": to,
"token_ids": token_ids,
"amounts": amounts.iter().map(|a| a.to_string()).collect::<Vec<_>>(),
});
let response = self.client.call("acc1155_mintBatch", params).await?;
let mint_data = &response["result"];
let batch_mint = BatchMint {
to: *to,
token_ids,
amounts,
minted_at: Timestamp::from_secs(
mint_data["minted_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing minted_at".to_string()))?
),
constitutional_receipt: Hash::from_hex(
mint_data["constitutional_receipt"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing constitutional_receipt".to_string()))?
).map_err(|e| NACError::InvalidHash(format!("Invalid receipt hash: {}", e)))?,
};
Ok(batch_mint)
}
/// 批量转移代币
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `from` - 发送者地址
/// * `to` - 接收者地址
/// * `token_ids` - 代币类型ID列表
/// * `amounts` - 数量列表
///
/// # 返回
/// 批量转移记录
pub async fn safe_transfer_batch(
&self,
certificate_address: &Address,
from: &Address,
to: &Address,
token_ids: Vec<TokenId>,
amounts: Vec<u128>,
) -> Result<BatchTransfer> {
let params = json!({
"certificate_address": certificate_address,
"from": from,
"to": to,
"token_ids": token_ids,
"amounts": amounts.iter().map(|a| a.to_string()).collect::<Vec<_>>(),
});
let response = self.client.call("acc1155_safeTransferBatch", params).await?;
let transfer_data = &response["result"];
let batch_transfer = BatchTransfer {
from: *from,
to: *to,
token_ids,
amounts,
transferred_at: Timestamp::from_secs(
transfer_data["transferred_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing transferred_at".to_string()))?
),
constitutional_receipt: Hash::from_hex(
transfer_data["constitutional_receipt"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing constitutional_receipt".to_string()))?
).map_err(|e| NACError::InvalidHash(format!("Invalid receipt hash: {}", e)))?,
};
Ok(batch_transfer)
}
/// 批量销毁代币
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `from` - 持有者地址
/// * `token_ids` - 代币类型ID列表
/// * `amounts` - 数量列表
///
/// # 返回
/// 批量销毁记录
pub async fn burn_batch(
&self,
certificate_address: &Address,
from: &Address,
token_ids: Vec<TokenId>,
amounts: Vec<u128>,
) -> Result<BatchBurn> {
let params = json!({
"certificate_address": certificate_address,
"from": from,
"token_ids": token_ids,
"amounts": amounts.iter().map(|a| a.to_string()).collect::<Vec<_>>(),
});
let response = self.client.call("acc1155_burnBatch", params).await?;
let burn_data = &response["result"];
let batch_burn = BatchBurn {
from: *from,
token_ids,
amounts,
burned_at: Timestamp::from_secs(
burn_data["burned_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing burned_at".to_string()))?
),
constitutional_receipt: Hash::from_hex(
burn_data["constitutional_receipt"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing constitutional_receipt".to_string()))?
).map_err(|e| NACError::InvalidHash(format!("Invalid receipt hash: {}", e)))?,
};
Ok(batch_burn)
}
/// 设置授权
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `owner` - 所有者地址
/// * `operator` - 操作者地址
/// * `approved` - 是否授权
pub async fn set_approval_for_all(
&self,
certificate_address: &Address,
owner: &Address,
operator: &Address,
approved: bool,
) -> Result<()> {
let params = json!({
"certificate_address": certificate_address,
"owner": owner,
"operator": operator,
"approved": approved,
});
self.client.call("acc1155_setApprovalForAll", params).await?;
Ok(())
}
/// 检查授权状态
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `owner` - 所有者地址
/// * `operator` - 操作者地址
///
/// # 返回
/// 是否已授权
pub async fn is_approved_for_all(
&self,
certificate_address: &Address,
owner: &Address,
operator: &Address,
) -> Result<bool> {
let params = json!({
"certificate_address": certificate_address,
"owner": owner,
"operator": operator,
});
let response = self.client.call("acc1155_isApprovedForAll", params).await?;
let approved = response["result"]
.as_bool()
.ok_or(NACError::InvalidResponse("Missing approval status".to_string()))?;
Ok(approved)
}
/// 获取代币类型元数据
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `token_id` - 代币类型ID
///
/// # 返回
/// 代币类型元数据
pub async fn get_token_metadata(
&self,
certificate_address: &Address,
token_id: TokenId,
) -> Result<TokenTypeMetadata> {
let params = json!({
"certificate_address": certificate_address,
"token_id": token_id,
});
let response = self.client.call("acc1155_getTokenMetadata", params).await?;
let meta_data = &response["result"];
let metadata = TokenTypeMetadata {
token_id,
name: meta_data["name"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing name".to_string()))?
.to_string(),
symbol: meta_data["symbol"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing symbol".to_string()))?
.to_string(),
token_type: serde_json::from_value(meta_data["token_type"].clone())
.map_err(|e| NACError::InvalidResponse(format!("Invalid token_type: {}", e)))?,
uri: meta_data["uri"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing uri".to_string()))?
.to_string(),
max_supply: meta_data["max_supply"]
.as_str()
.and_then(|s| s.parse().ok()),
current_supply: meta_data["current_supply"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing current_supply".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid current_supply: {}", e)))?,
created_at: Timestamp::from_secs(
meta_data["created_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing created_at".to_string()))?
),
};
Ok(metadata)
}
/// 获取代币类型DNA
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `token_id` - 代币类型ID
///
/// # 返回
/// 代币类型DNA
pub async fn get_token_dna(
&self,
certificate_address: &Address,
token_id: TokenId,
) -> Result<TokenTypeDNA> {
let params = json!({
"certificate_address": certificate_address,
"token_id": token_id,
});
let response = self.client.call("acc1155_getTokenDNA", params).await?;
let dna_data = &response["result"];
let token_dna = TokenTypeDNA {
dna_hash: Hash::from_hex(
dna_data["dna_hash"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing dna_hash".to_string()))?
).map_err(|e| NACError::InvalidHash(format!("Invalid dna_hash: {}", e)))?,
token_id,
gnacs_code: serde_json::from_value(dna_data["gnacs_code"].clone())
.map_err(|e| NACError::InvalidResponse(format!("Invalid gnacs_code: {}", e)))?,
token_type: serde_json::from_value(dna_data["token_type"].clone())
.map_err(|e| NACError::InvalidResponse(format!("Invalid token_type: {}", e)))?,
sovereignty_type: serde_json::from_value(dna_data["sovereignty_type"].clone())
.map_err(|e| NACError::InvalidResponse(format!("Invalid sovereignty_type: {}", e)))?,
metadata_hash: Hash::from_hex(
dna_data["metadata_hash"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing metadata_hash".to_string()))?
).map_err(|e| NACError::InvalidHash(format!("Invalid metadata_hash: {}", e)))?,
generated_at: Timestamp::from_secs(
dna_data["generated_at"]
.as_u64()
.ok_or(NACError::InvalidResponse("Missing generated_at".to_string()))?
),
};
Ok(token_dna)
}
/// 获取托管信息
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `token_id` - 代币类型ID
///
/// # 返回
/// 托管信息
pub async fn get_custody_info(
&self,
certificate_address: &Address,
token_id: TokenId,
) -> Result<TokenCustodyInfo> {
let params = json!({
"certificate_address": certificate_address,
"token_id": token_id,
});
let response = self.client.call("acc1155_getCustodyInfo", params).await?;
let custody_data = &response["result"];
let custody_info = TokenCustodyInfo {
token_id,
custodian: Address::from_hex(
custody_data["custodian"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing custodian".to_string()))?
).map_err(|e| NACError::InvalidAddress(format!("Invalid custodian: {}", e)))?,
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()))?
).map_err(|e| NACError::InvalidHash(format!("Invalid custody_proof: {}", e)))?,
};
Ok(custody_info)
}
/// 获取保险信息
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `token_id` - 代币类型ID
///
/// # 返回
/// 保险信息
pub async fn get_insurance_info(
&self,
certificate_address: &Address,
token_id: TokenId,
) -> Result<TokenInsuranceInfo> {
let params = json!({
"certificate_address": certificate_address,
"token_id": token_id,
});
let response = self.client.call("acc1155_getInsuranceInfo", params).await?;
let insurance_data = &response["result"];
let insurance_info = TokenInsuranceInfo {
token_id,
insurer: Address::from_hex(
insurance_data["insurer"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing insurer".to_string()))?
).map_err(|e| NACError::InvalidAddress(format!("Invalid insurer: {}", e)))?,
coverage_per_unit_xtzh: insurance_data["coverage_per_unit_xtzh"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing coverage_per_unit_xtzh".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid coverage: {}", 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)
}
/// 更新代币类型估值
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `token_id` - 代币类型ID
/// * `valuation` - 新的估值
pub async fn update_token_valuation(
&self,
certificate_address: &Address,
token_id: TokenId,
valuation: TokenTypeValuation,
) -> Result<()> {
let params = json!({
"certificate_address": certificate_address,
"token_id": token_id,
"valuation": {
"value_per_unit_xtzh": valuation.value_per_unit_xtzh.to_string(),
"valuation_provider": valuation.valuation_provider,
"valued_at": valuation.valued_at.as_secs(),
"validity_period": valuation.validity_period,
},
});
self.client.call("acc1155_updateTokenValuation", params).await?;
Ok(())
}
/// 获取代币类型估值
///
/// # 参数
/// * `certificate_address` - 证书地址
/// * `token_id` - 代币类型ID
///
/// # 返回
/// 代币类型估值
pub async fn get_token_valuation(
&self,
certificate_address: &Address,
token_id: TokenId,
) -> Result<TokenTypeValuation> {
let params = json!({
"certificate_address": certificate_address,
"token_id": token_id,
});
let response = self.client.call("acc1155_getTokenValuation", params).await?;
let val_data = &response["result"];
let valuation = TokenTypeValuation {
token_id,
value_per_unit_xtzh: val_data["value_per_unit_xtzh"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing value_per_unit_xtzh".to_string()))?
.parse()
.map_err(|e| NACError::InvalidResponse(format!("Invalid value: {}", e)))?,
valuation_provider: Address::from_hex(
val_data["valuation_provider"]
.as_str()
.ok_or(NACError::InvalidResponse("Missing valuation_provider".to_string()))?
).map_err(|e| NACError::InvalidAddress(format!("Invalid valuation_provider: {}", e)))?,
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)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_acc1155_interface() {
// 这里只是接口测试实际需要连接到NAC节点
let client = NRPC3Client::new("https://rpc.newassetchain.io");
let acc1155 = ACC1155::new(client);
// 测试将在实际连接到NAC节点后进行
assert!(true);
}
}