772 lines
20 KiB
Plaintext
772 lines
20 KiB
Plaintext
///! # ACC-721协议
|
||
///!
|
||
///! Asset Certificate Contract - 721 (ACC-721)
|
||
///! NAC的唯一资产协议(类似ERC-721 NFT,但专为RWA设计)
|
||
///!
|
||
///! **版本**: v1.0
|
||
///! **模块**: charter-std/acc/acc721.ch
|
||
|
||
use asset::gnacs::GNACSCode;
|
||
use sovereignty::rules::SovereigntyType;
|
||
|
||
// ============================================================================
|
||
// ACC-721接口定义
|
||
// ============================================================================
|
||
|
||
/// ACC-721唯一资产接口
|
||
///
|
||
/// 定义唯一资产(NFT)的标准操作
|
||
interface ACC721 {
|
||
// ========== 查询函数 ==========
|
||
|
||
/// 查询资产总数
|
||
///
|
||
/// # 返回
|
||
/// - `u256`: 资产总数
|
||
fn totalSupply() -> u256;
|
||
|
||
/// 查询所有者的资产数量
|
||
///
|
||
/// # 参数
|
||
/// - `owner`: 所有者地址
|
||
///
|
||
/// # 返回
|
||
/// - `u256`: 资产数量
|
||
fn holdingsCount(owner: Address) -> u256;
|
||
|
||
/// 查询资产所有者
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `Address`: 所有者地址
|
||
fn ownerOf(asset_id: u256) -> Address;
|
||
|
||
/// 查询资产是否存在
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否存在
|
||
fn exists(asset_id: u256) -> bool;
|
||
|
||
/// 查询集合名称
|
||
///
|
||
/// # 返回
|
||
/// - `String`: 集合名称
|
||
fn name() -> String;
|
||
|
||
/// 查询集合符号
|
||
///
|
||
/// # 返回
|
||
/// - `String`: 集合符号
|
||
fn symbol() -> String;
|
||
|
||
/// 查询资产URI
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `String`: 资产URI
|
||
fn assetURI(asset_id: u256) -> String;
|
||
|
||
/// 查询GNACS编码
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `u48`: GNACS编码
|
||
fn gnacsCode(asset_id: u256) -> u48;
|
||
|
||
/// 查询主权类型
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `SovereigntyType`: 主权类型
|
||
fn sovereigntyType(asset_id: u256) -> SovereigntyType;
|
||
|
||
// ========== 转账函数 ==========
|
||
|
||
/// 转移资产
|
||
///
|
||
/// # 参数
|
||
/// - `to`: 接收方地址
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否成功
|
||
fn transfer(to: Address, asset_id: u256) -> bool;
|
||
|
||
/// 安全转移资产(检查接收方)
|
||
///
|
||
/// # 参数
|
||
/// - `to`: 接收方地址
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否成功
|
||
fn safeTransfer(to: Address, asset_id: u256) -> bool;
|
||
|
||
/// 从授权转移资产
|
||
///
|
||
/// # 参数
|
||
/// - `from`: 发送方地址
|
||
/// - `to`: 接收方地址
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否成功
|
||
fn transferFrom(from: Address, to: Address, asset_id: u256) -> bool;
|
||
|
||
// ========== 授权函数 ==========
|
||
|
||
/// 授权单个资产
|
||
///
|
||
/// # 参数
|
||
/// - `approved`: 被授权方地址
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否成功
|
||
fn approve(approved: Address, asset_id: u256) -> bool;
|
||
|
||
/// 查询资产授权
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `Address`: 被授权方地址
|
||
fn getApproved(asset_id: u256) -> Address;
|
||
|
||
/// 授权所有资产
|
||
///
|
||
/// # 参数
|
||
/// - `operator`: 操作员地址
|
||
/// - `approved`: 是否授权
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否成功
|
||
fn setApprovalForAll(operator: Address, approved: bool) -> bool;
|
||
|
||
/// 查询操作员授权
|
||
///
|
||
/// # 参数
|
||
/// - `owner`: 所有者地址
|
||
/// - `operator`: 操作员地址
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否授权
|
||
fn isApprovedForAll(owner: Address, operator: Address) -> bool;
|
||
|
||
// ========== RWA扩展函数 ==========
|
||
|
||
/// 冻结资产
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否成功
|
||
fn freeze(asset_id: u256) -> bool;
|
||
|
||
/// 解冻资产
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否成功
|
||
fn unfreeze(asset_id: u256) -> bool;
|
||
|
||
/// 检查资产是否冻结
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否冻结
|
||
fn isFrozen(asset_id: u256) -> bool;
|
||
|
||
/// 查询合规状态
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `u4`: 合规状态
|
||
fn complianceStatus(asset_id: u256) -> u4;
|
||
}
|
||
|
||
// ============================================================================
|
||
// ACC-721事件定义
|
||
// ============================================================================
|
||
|
||
/// 转移事件
|
||
event Transfer {
|
||
from: Address,
|
||
to: Address,
|
||
asset_id: u256,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
/// 授权事件
|
||
event Approval {
|
||
owner: Address,
|
||
approved: Address,
|
||
asset_id: u256,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
/// 操作员授权事件
|
||
event ApprovalForAll {
|
||
owner: Address,
|
||
operator: Address,
|
||
approved: bool,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
/// 铸造事件
|
||
event Mint {
|
||
to: Address,
|
||
asset_id: u256,
|
||
gnacs_code: u48,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
/// 销毁事件
|
||
event Burn {
|
||
from: Address,
|
||
asset_id: u256,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
/// 冻结事件
|
||
event Freeze {
|
||
asset_id: u256,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
/// 解冻事件
|
||
event Unfreeze {
|
||
asset_id: u256,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
/// URI更新事件
|
||
event URIUpdate {
|
||
asset_id: u256,
|
||
new_uri: String,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
// ============================================================================
|
||
// 资产信息结构
|
||
// ============================================================================
|
||
|
||
/// 资产信息
|
||
struct AssetInfo {
|
||
/// 资产ID
|
||
asset_id: u256,
|
||
|
||
/// 所有者
|
||
owner: Address,
|
||
|
||
/// GNACS编码
|
||
gnacs_code: u48,
|
||
|
||
/// 主权类型
|
||
sovereignty_type: SovereigntyType,
|
||
|
||
/// 资产URI
|
||
uri: String,
|
||
|
||
/// 创建时间
|
||
created_at: Timestamp,
|
||
|
||
/// 是否冻结
|
||
is_frozen: bool,
|
||
|
||
/// 合规状态
|
||
compliance_status: u4
|
||
}
|
||
|
||
// ============================================================================
|
||
// ACC-721标准实现
|
||
// ============================================================================
|
||
|
||
/// ACC-721标准实现
|
||
///
|
||
/// 唯一资产的标准实现
|
||
certificate ACC721Asset with Sovereignty<A0> implements ACC721 {
|
||
// ========== 状态变量 ==========
|
||
|
||
/// 集合名称
|
||
let _name: String;
|
||
|
||
/// 集合符号
|
||
let _symbol: String;
|
||
|
||
/// 基础URI
|
||
let _base_uri: String;
|
||
|
||
/// 资产总数
|
||
let _total_supply: u256;
|
||
|
||
/// 下一个资产ID
|
||
let _next_asset_id: u256;
|
||
|
||
/// 资产信息映射 (asset_id => AssetInfo)
|
||
let _assets: Map<u256, AssetInfo>;
|
||
|
||
/// 所有者资产列表 (owner => asset_ids)
|
||
let _owner_assets: Map<Address, Set<u256>>;
|
||
|
||
/// 资产授权 (asset_id => approved_address)
|
||
let _asset_approvals: Map<u256, Address>;
|
||
|
||
/// 操作员授权 (owner => operator => approved)
|
||
let _operator_approvals: Map<Address, Map<Address, bool>>;
|
||
|
||
/// 管理员地址
|
||
let _admin: Address;
|
||
|
||
// ========== 构造函数 ==========
|
||
|
||
/// 构造函数
|
||
///
|
||
/// # 参数
|
||
/// - `name`: 集合名称
|
||
/// - `symbol`: 集合符号
|
||
/// - `base_uri`: 基础URI
|
||
constructor(
|
||
name: String,
|
||
symbol: String,
|
||
base_uri: String
|
||
) {
|
||
require(!name.is_empty(), "Name cannot be empty");
|
||
require(!symbol.is_empty(), "Symbol cannot be empty");
|
||
|
||
self._name = name;
|
||
self._symbol = symbol;
|
||
self._base_uri = base_uri;
|
||
self._total_supply = 0;
|
||
self._next_asset_id = 1;
|
||
self._admin = msg.sender;
|
||
}
|
||
|
||
// ========== 查询函数实现 ==========
|
||
|
||
fn totalSupply() -> u256 {
|
||
return self._total_supply;
|
||
}
|
||
|
||
fn holdingsCount(owner: Address) -> u256 {
|
||
return self._owner_assets.get(owner)
|
||
.map(|set| set.len())
|
||
.unwrap_or(0) as u256;
|
||
}
|
||
|
||
fn ownerOf(asset_id: u256) -> Address {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
return self._assets[asset_id].owner;
|
||
}
|
||
|
||
fn exists(asset_id: u256) -> bool {
|
||
return self._assets.contains_key(asset_id);
|
||
}
|
||
|
||
fn name() -> String {
|
||
return self._name;
|
||
}
|
||
|
||
fn symbol() -> String {
|
||
return self._symbol;
|
||
}
|
||
|
||
fn assetURI(asset_id: u256) -> String {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
|
||
let asset = self._assets[asset_id];
|
||
|
||
if !asset.uri.is_empty() {
|
||
return asset.uri;
|
||
}
|
||
|
||
// 使用基础URI + asset_id
|
||
return self._base_uri + "/" + asset_id.to_string();
|
||
}
|
||
|
||
fn gnacsCode(asset_id: u256) -> u48 {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
return self._assets[asset_id].gnacs_code;
|
||
}
|
||
|
||
fn sovereigntyType(asset_id: u256) -> SovereigntyType {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
return self._assets[asset_id].sovereignty_type;
|
||
}
|
||
|
||
// ========== 转账函数实现 ==========
|
||
|
||
fn transfer(to: Address, asset_id: u256) -> bool {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
require(!to.is_zero(), "Transfer to zero address");
|
||
require(!self.isFrozen(asset_id), "Asset is frozen");
|
||
|
||
let owner = self.ownerOf(asset_id);
|
||
require(msg.sender == owner, "Not the owner");
|
||
|
||
return self._transfer(owner, to, asset_id);
|
||
}
|
||
|
||
fn safeTransfer(to: Address, asset_id: u256) -> bool {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
require(!to.is_zero(), "Transfer to zero address");
|
||
require(!self.isFrozen(asset_id), "Asset is frozen");
|
||
|
||
let owner = self.ownerOf(asset_id);
|
||
require(msg.sender == owner, "Not the owner");
|
||
|
||
// 检查接收方是否为合约
|
||
if to.is_contract() {
|
||
// 检查接收方是否实现了ACC721Receiver接口
|
||
require(
|
||
self._check_receiver(to, owner, asset_id),
|
||
"Receiver not implemented"
|
||
);
|
||
}
|
||
|
||
return self._transfer(owner, to, asset_id);
|
||
}
|
||
|
||
fn transferFrom(from: Address, to: Address, asset_id: u256) -> bool {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
require(!from.is_zero(), "Transfer from zero address");
|
||
require(!to.is_zero(), "Transfer to zero address");
|
||
require(!self.isFrozen(asset_id), "Asset is frozen");
|
||
|
||
let owner = self.ownerOf(asset_id);
|
||
require(from == owner, "From is not the owner");
|
||
|
||
// 检查授权
|
||
require(
|
||
self._is_approved_or_owner(msg.sender, asset_id),
|
||
"Not approved or owner"
|
||
);
|
||
|
||
return self._transfer(from, to, asset_id);
|
||
}
|
||
|
||
// ========== 授权函数实现 ==========
|
||
|
||
fn approve(approved: Address, asset_id: u256) -> bool {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
|
||
let owner = self.ownerOf(asset_id);
|
||
require(msg.sender == owner, "Not the owner");
|
||
require(approved != owner, "Approve to owner");
|
||
|
||
self._asset_approvals[asset_id] = approved;
|
||
|
||
emit Approval {
|
||
owner: owner,
|
||
approved: approved,
|
||
asset_id: asset_id,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return true;
|
||
}
|
||
|
||
fn getApproved(asset_id: u256) -> Address {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
return self._asset_approvals.get(asset_id).unwrap_or(Address::zero());
|
||
}
|
||
|
||
fn setApprovalForAll(operator: Address, approved: bool) -> bool {
|
||
require(!operator.is_zero(), "Operator is zero address");
|
||
require(operator != msg.sender, "Approve to self");
|
||
|
||
self._operator_approvals[msg.sender][operator] = approved;
|
||
|
||
emit ApprovalForAll {
|
||
owner: msg.sender,
|
||
operator: operator,
|
||
approved: approved,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return true;
|
||
}
|
||
|
||
fn isApprovedForAll(owner: Address, operator: Address) -> bool {
|
||
return self._operator_approvals.get(owner)
|
||
.and_then(|m| m.get(operator))
|
||
.unwrap_or(false);
|
||
}
|
||
|
||
// ========== RWA扩展函数实现 ==========
|
||
|
||
fn freeze(asset_id: u256) -> bool {
|
||
require(msg.sender == self._admin, "Only admin can freeze");
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
|
||
let mut asset = self._assets[asset_id];
|
||
asset.is_frozen = true;
|
||
self._assets[asset_id] = asset;
|
||
|
||
emit Freeze {
|
||
asset_id: asset_id,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return true;
|
||
}
|
||
|
||
fn unfreeze(asset_id: u256) -> bool {
|
||
require(msg.sender == self._admin, "Only admin can unfreeze");
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
|
||
let mut asset = self._assets[asset_id];
|
||
asset.is_frozen = false;
|
||
self._assets[asset_id] = asset;
|
||
|
||
emit Unfreeze {
|
||
asset_id: asset_id,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return true;
|
||
}
|
||
|
||
fn isFrozen(asset_id: u256) -> bool {
|
||
if !self.exists(asset_id) {
|
||
return false;
|
||
}
|
||
return self._assets[asset_id].is_frozen;
|
||
}
|
||
|
||
fn complianceStatus(asset_id: u256) -> u4 {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
return self._assets[asset_id].compliance_status;
|
||
}
|
||
|
||
// ========== 内部函数 ==========
|
||
|
||
/// 内部转账函数
|
||
fn _transfer(from: Address, to: Address, asset_id: u256) -> bool {
|
||
// 从原所有者移除
|
||
self._owner_assets[from].remove(asset_id);
|
||
|
||
// 添加到新所有者
|
||
if !self._owner_assets.contains_key(to) {
|
||
self._owner_assets[to] = Set::new();
|
||
}
|
||
self._owner_assets[to].insert(asset_id);
|
||
|
||
// 更新资产所有者
|
||
let mut asset = self._assets[asset_id];
|
||
asset.owner = to;
|
||
self._assets[asset_id] = asset;
|
||
|
||
// 清除授权
|
||
self._asset_approvals.remove(asset_id);
|
||
|
||
emit Transfer {
|
||
from: from,
|
||
to: to,
|
||
asset_id: asset_id,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return true;
|
||
}
|
||
|
||
/// 检查是否为授权或所有者
|
||
fn _is_approved_or_owner(spender: Address, asset_id: u256) -> bool {
|
||
let owner = self.ownerOf(asset_id);
|
||
|
||
return spender == owner ||
|
||
self.getApproved(asset_id) == spender ||
|
||
self.isApprovedForAll(owner, spender);
|
||
}
|
||
|
||
/// 检查接收方
|
||
fn _check_receiver(receiver: Address, from: Address, asset_id: u256) -> bool {
|
||
// 调用接收方的onACC721Received函数
|
||
// 简化实现,实际需要通过接口调用
|
||
return true;
|
||
}
|
||
|
||
// ========== 管理函数 ==========
|
||
|
||
/// 铸造新资产
|
||
///
|
||
/// # 参数
|
||
/// - `to`: 接收方地址
|
||
/// - `gnacs_code`: GNACS编码
|
||
/// - `uri`: 资产URI
|
||
///
|
||
/// # 返回
|
||
/// - `u256`: 资产ID
|
||
pub fn mint(
|
||
to: Address,
|
||
gnacs_code: u48,
|
||
uri: String
|
||
) -> u256 {
|
||
require(msg.sender == self._admin, "Only admin can mint");
|
||
require(!to.is_zero(), "Mint to zero address");
|
||
|
||
// 验证GNACS编码
|
||
let gnacs = GNACSCode::from_u48(gnacs_code);
|
||
require(gnacs.validate(), "Invalid GNACS code");
|
||
|
||
let asset_id = self._next_asset_id;
|
||
self._next_asset_id += 1;
|
||
|
||
// 创建资产信息
|
||
let asset = AssetInfo {
|
||
asset_id: asset_id,
|
||
owner: to,
|
||
gnacs_code: gnacs_code,
|
||
sovereignty_type: SovereigntyType::A0,
|
||
uri: uri,
|
||
created_at: block.timestamp,
|
||
is_frozen: false,
|
||
compliance_status: 0
|
||
};
|
||
|
||
self._assets[asset_id] = asset;
|
||
|
||
// 添加到所有者资产列表
|
||
if !self._owner_assets.contains_key(to) {
|
||
self._owner_assets[to] = Set::new();
|
||
}
|
||
self._owner_assets[to].insert(asset_id);
|
||
|
||
self._total_supply += 1;
|
||
|
||
emit Mint {
|
||
to: to,
|
||
asset_id: asset_id,
|
||
gnacs_code: gnacs_code,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return asset_id;
|
||
}
|
||
|
||
/// 销毁资产
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否成功
|
||
pub fn burn(asset_id: u256) -> bool {
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
|
||
let owner = self.ownerOf(asset_id);
|
||
require(
|
||
msg.sender == owner || msg.sender == self._admin,
|
||
"Not owner or admin"
|
||
);
|
||
|
||
// 从所有者资产列表移除
|
||
self._owner_assets[owner].remove(asset_id);
|
||
|
||
// 删除资产
|
||
self._assets.remove(asset_id);
|
||
|
||
// 清除授权
|
||
self._asset_approvals.remove(asset_id);
|
||
|
||
self._total_supply -= 1;
|
||
|
||
emit Burn {
|
||
from: owner,
|
||
asset_id: asset_id,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return true;
|
||
}
|
||
|
||
/// 更新资产URI
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
/// - `new_uri`: 新URI
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否成功
|
||
pub fn updateURI(asset_id: u256, new_uri: String) -> bool {
|
||
require(msg.sender == self._admin, "Only admin can update URI");
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
|
||
let mut asset = self._assets[asset_id];
|
||
asset.uri = new_uri.clone();
|
||
self._assets[asset_id] = asset;
|
||
|
||
emit URIUpdate {
|
||
asset_id: asset_id,
|
||
new_uri: new_uri,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return true;
|
||
}
|
||
|
||
/// 设置合规状态
|
||
///
|
||
/// # 参数
|
||
/// - `asset_id`: 资产ID
|
||
/// - `status`: 合规状态
|
||
///
|
||
/// # 返回
|
||
/// - `bool`: 是否成功
|
||
pub fn setComplianceStatus(asset_id: u256, status: u4) -> bool {
|
||
require(msg.sender == self._admin, "Only admin");
|
||
require(self.exists(asset_id), "Asset does not exist");
|
||
|
||
let mut asset = self._assets[asset_id];
|
||
asset.compliance_status = status;
|
||
self._assets[asset_id] = asset;
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// ACC-721接收器接口
|
||
// ============================================================================
|
||
|
||
/// ACC-721接收器接口
|
||
///
|
||
/// 合约必须实现此接口才能接收ACC-721资产
|
||
interface ACC721Receiver {
|
||
/// 接收ACC-721资产
|
||
///
|
||
/// # 参数
|
||
/// - `operator`: 操作员地址
|
||
/// - `from`: 发送方地址
|
||
/// - `asset_id`: 资产ID
|
||
/// - `data`: 附加数据
|
||
///
|
||
/// # 返回
|
||
/// - `bytes4`: 函数选择器
|
||
fn onACC721Received(
|
||
operator: Address,
|
||
from: Address,
|
||
asset_id: u256,
|
||
data: Bytes
|
||
) -> bytes4;
|
||
}
|