pragmasolidity^0.8.4;import"@openzeppelin/contracts/utils/introspection/IERC165.sol";interfaceIERC_5521isIERC165{/// Logged when a node in the rNFT gets referred and changed.
/// @notice Emitted when the `node` (i.e., an rNFT) is changed.
// 当 rNFT 中的节点被引用和更改时记录。
// @notice 当 `node`(即 rNFT)更改时发出。
eventUpdateNode(uint256indexedtokenId,addressindexedowner,address[]_address_referringList,uint256[][]_tokenIds_referringList,address[]_address_referredList,uint256[][]_tokenIds_referredList);/// @notice set the referred list of an rNFT associated with different contract addresses and update the referring list of each one in the referred list. Checking the duplication of `addresses` and `tokenIds` is **RECOMMENDED**.
// @notice 设置与不同合约地址关联的 rNFT 的被引用列表,并更新被引用列表中每个合约地址的引用列表。**建议**检查 `addresses` 和 `tokenIds` 是否重复。
/// @param `tokenId` of rNFT being set. `addresses` of the contracts in which rNFTs with `tokenIds` being referred accordingly.
// @param 要设置的 rNFT 的 `tokenId`。相应地,rNFT 与 `tokenIds` 被引用的合约的 `addresses`。
/// @requirement
// @requirement (要求)
/// - the size of `addresses` **MUST** be the same as that of `tokenIds`;
// - `addresses` 的大小**必须**与 `tokenIds` 的大小相同。
/// - once the size of `tokenIds` is non-zero, the inner size **MUST** also be non-zero;
// - 一旦 `tokenIds` 的大小非零,则内部大小**必须**也非零。
/// - the `tokenId` **MUST** be unique within the same contract;
// - `tokenId` 在同一合约中**必须**是唯一的。
/// - the `tokenId` **MUST NOT** be the same as `tokenIds[i][j]` if `addresses[i]` is essentially `address(this)`.
// - 如果 `addresses[i]` 本质上是 `address(this)`,则 `tokenId` **不得**与 `tokenIds[i][j]` 相同。
functionsetNode(uint256tokenId,address[]memoryaddresses,uint256[][]memorytokenIds)external;/// @notice get the referring list of an rNFT.
// @notice 获取 rNFT 的引用列表。
/// @param `tokenId` of the rNFT being focused, `_address` of contract address associated with the focused rNFT.
// @param 关注的 rNFT 的 `tokenId`,与关注的 rNFT 关联的合约地址 `_address`。
/// @return the referring mapping of the rNFT.
// @return rNFT 的引用映射。
functionreferringOf(address_address,uint256tokenId)externalviewreturns(address[]memory,uint256[][]memory);/// @notice get the referred list of an rNFT.
// @notice 获取 rNFT 的被引用列表。
/// @param `tokenId` of the rNFT being focused, `_address` of contract address associated with the focused rNFT.
// @param 关注的 rNFT 的 `tokenId`,与关注的 rNFT 关联的合约地址 `_address`。
/// @return the referred mapping of the rNFT.
// @return rNFT 的被引用映射。
functionreferredOf(address_address,uint256tokenId)externalviewreturns(address[]memory,uint256[][]memory);/// @notice get the timestamp of an rNFT when is being created.
// @notice 获取 rNFT 创建时的时间戳。
/// @param `tokenId` of the rNFT being focused, `_address` of contract address associated with the focused rNFT.
// @param 关注的 rNFT 的 `tokenId`,与关注的 rNFT 关联的合约地址 `_address`。
/// @return the timestamp of the rNFT when is being created with uint256 format.
// @return rNFT 创建时的时间戳,格式为 uint256。
functioncreatedTimestampOf(address_address,uint256tokenId)externalviewreturns(uint256);/// @notice check supported interfaces, adhereing to ERC165.
// @notice 检查支持的接口,遵守 ERC165。
functionsupportsInterface(bytes4interfaceId)externalviewreturns(bool);}interfaceTargetContractisIERC165{/// @notice set the referred list of an rNFT associated with external contract addresses.
// @notice 设置与外部合约地址关联的 rNFT 的被引用列表。
/// @param `_tokenIds` of rNFTs associated with the contract address `_address` being referred by the rNFT with `tokenId`.
// @param 与合约地址 `_address` 关联的 rNFT 的 `_tokenIds`,这些 rNFT 被具有 `tokenId` 的 rNFT 引用。
/// @requirement
// @requirement (要求)
/// - `_address` **MUST NOT** be the same as `address(this)` where `this` is executed by an external contract where `TargetContract` interface is implemented.
// - `_address` **不得**与 `address(this)` 相同,其中 `this` 由实现了 `TargetContract` 接口的外部合约执行。
functionsetNodeReferredExternal(address_address,uint256tokenId,uint256[]memory_tokenIds)external;functionreferringOf(address_address,uint256tokenId)externalviewreturns(address[]memory,uint256[][]memory);functionreferredOf(address_address,uint256tokenId)externalviewreturns(address[]memory,uint256[][]memory);functioncreatedTimestampOf(address_address,uint256tokenId)externalviewreturns(uint256);functionsupportsInterface(bytes4interfaceId)externalviewreturns(bool);}
pragmasolidity^0.8.4;import"@openzeppelin/contracts/token/ERC721/ERC721.sol";import"./IERC_5521.sol";contractERC_5521isERC721,IERC_5521,TargetContract{structRelationship{mapping(address=>uint256[])referring;mapping(address=>uint256[])referred;address[]referringKeys;address[]referredKeys;uint256createdTimestamp;// unix timestamp when the rNFT is being created
// rNFT 创建时的 Unix 时间戳
// extensible parameters
// 可扩展参数
// ...
}mapping(uint256=>Relationship)internal_relationship;addresscontractOwner=address(0);constructor(stringmemoryname_,stringmemorysymbol_)ERC721(name_,symbol_){contractOwner=msg.sender;}functionsafeMint(uint256tokenId,address[]memoryaddresses,uint256[][]memory_tokenIds)public{// require(msg.sender == contractOwner, "ERC_rNFT: Only contract owner can mint");
// require(msg.sender == contractOwner, "ERC_rNFT: 只有合约所有者才能铸造");
_safeMint(msg.sender,tokenId);setNode(tokenId,addresses,_tokenIds);}/// @notice set the referred list of an rNFT associated with different contract addresses and update the referring list of each one in the referred list
// @notice 设置与不同合约地址关联的 rNFT 的被引用列表,并更新被引用列表中每个合约地址的引用列表
/// @param tokenIds array of rNFTs, recommended to check duplication at the caller's end
// @param tokenIds rNFT 数组,建议在调用者端检查重复项
functionsetNode(uint256tokenId,address[]memoryaddresses,uint256[][]memorytokenIds)publicvirtualoverride{require(addresses.length==tokenIds.length,"Addresses and TokenID arrays must have the same length"// "地址和 TokenID 数组的长度必须相同"
);for(uinti=0;i<tokenIds.length;i++){if(tokenIds[i].length==0){revert("ERC_5521: the referring list cannot be empty");}// if (tokenIds[i].length == 0) { revert("ERC_5521: 引用列表不能为空"); }
}setNodeReferring(addresses,tokenId,tokenIds);setNodeReferred(addresses,tokenId,tokenIds);}/// @notice set the referring list of an rNFT associated with different contract addresses
// @notice 设置与不同合约地址关联的 rNFT 的引用列表
/// @param _tokenIds array of rNFTs associated with addresses, recommended to check duplication at the caller's end
// @param _tokenIds 与地址关联的 rNFT 数组,建议在调用者端检查重复项
functionsetNodeReferring(address[]memoryaddresses,uint256tokenId,uint256[][]memory_tokenIds)private{require(_isApprovedOrOwner(msg.sender,tokenId),"ERC_5521: transfer caller is not owner nor approved");// require(_isApprovedOrOwner(msg.sender, tokenId), "ERC_5521: 转移调用者不是所有者,也没有获得批准");
Relationshipstoragerelationship=_relationship[tokenId];for(uinti=0;i<addresses.length;i++){if(relationship.referring[addresses[i]].length==0){relationship.referringKeys.push(addresses[i]);}// Add the address if it's a new entry
// if (relationship.referring[addresses[i]].length == 0) { relationship.referringKeys.push(addresses[i]); } // 如果是新条目,则添加地址
relationship.referring[addresses[i]]=_tokenIds[i];}relationship.createdTimestamp=block.timestamp;emitEvents(tokenId,msg.sender);}/// @notice set the referred list of an rNFT associated with different contract addresses
// @notice 设置与不同合约地址关联的 rNFT 的被引用列表
/// @param _tokenIds array of rNFTs associated with addresses, recommended to check duplication at the caller's end
// @param _tokenIds 与地址关联的 rNFT 数组,建议在调用者端检查重复项
functionsetNodeReferred(address[]memoryaddresses,uint256tokenId,uint256[][]memory_tokenIds)private{for(uinti=0;i<addresses.length;i++){if(addresses[i]==address(this)){for(uintj=0;j<_tokenIds[i].length;j++){Relationshipstoragerelationship=_relationship[_tokenIds[i][j]];if(relationship.referred[addresses[i]].length==0){relationship.referredKeys.push(addresses[i]);}// Add the address if it's a new entry
// if (relationship.referred[addresses[i]].length == 0) { relationship.referredKeys.push(addresses[i]); } // 如果是新条目,则添加地址
require(tokenId!=_tokenIds[i][j],"ERC_5521: self-reference not allowed");// require(tokenId != _tokenIds[i][j], "ERC_5521: 不允许自引用");
if(relationship.createdTimestamp>=block.timestamp){revert("ERC_5521: the referred rNFT needs to be a predecessor");}// Make sure the reference complies with the timing sequence
// if (relationship.createdTimestamp >= block.timestamp) { revert("ERC_5521: 被引用的 rNFT 需要是一个前任"); } // 确保引用符合时序
relationship.referred[address(this)].push(tokenId);emitEvents(_tokenIds[i][j],ownerOf(_tokenIds[i][j]));}}else{TargetContracttargetContractInstance=TargetContract(addresses[i]);boolisSupports=targetContractInstance.supportsInterface(type(TargetContract).interfaceId);if(isSupports){// The target contract supports the interface, safe to call functions of the interface.
// 目标合约支持该接口,可以安全地调用该接口的函数。
targetContractInstance.setNodeReferredExternal(address(this),tokenId,_tokenIds[i]);}}}}/// @notice set the referred list of an rNFT associated with different contract addresses
// @notice 设置与不同合约地址关联的 rNFT 的被引用列表
/// @param _tokenIds array of rNFTs associated with addresses, recommended to check duplication at the caller's end
// @param _tokenIds 与地址关联的 rNFT 数组,建议在调用者端检查重复项
functionsetNodeReferredExternal(address_address,uint256tokenId,uint256[]memory_tokenIds)external{for(uinti=0;i<_tokenIds.length;i++){Relationshipstoragerelationship=_relationship[_tokenIds[i]];if(relationship.referred[_address].length==0){relationship.referredKeys.push(_address);}// Add the address if it's a new entry
// if (relationship.referred[_address].length == 0) { relationship.referredKeys.push(_address); } // 如果是新条目,则添加地址
require(_address!=address(this),"ERC_5521: this must be an external contract address");// require(_address != address(this), "ERC_5521: 这必须是一个外部合约地址");
if(relationship.createdTimestamp>=block.timestamp){revert("ERC_5521: the referred rNFT needs to be a predecessor");}// Make sure the reference complies with the timing sequence
// if (relationship.createdTimestamp >= block.timestamp) { revert("ERC_5521: 被引用的 rNFT 需要是一个前任"); } // 确保引用符合时序
relationship.referred[_address].push(tokenId);emitEvents(_tokenIds[i],ownerOf(_tokenIds[i]));}}/// @notice Get the referring list of an rNFT
// @notice 获取 rNFT 的引用列表
/// @param tokenId The considered rNFT, _address The corresponding contract address
// @param tokenId 要考虑的 rNFT,_address 对应的合约地址
/// @return The referring mapping of an rNFT
// @return rNFT 的引用映射
functionreferringOf(address_address,uint256tokenId)externalviewvirtualoverride(IERC_5521,TargetContract)returns(address[]memory,uint256[][]memory){address[]memory_referringKeys;uint256[][]memory_referringValues;if(_address==address(this)){require(_exists(tokenId),"ERC_5521: token ID not existed");// require(_exists(tokenId), "ERC_5521: Token ID 不存在");
(_referringKeys,_referringValues)=convertMap(tokenId,true);}else{TargetContracttargetContractInstance=TargetContract(_address);require(targetContractInstance.supportsInterface(type(TargetContract).interfaceId),"ERC_5521: target contract not supported");// require(targetContractInstance.supportsInterface(type(TargetContract).interfaceId), "ERC_5521: 目标合约不支持");
(_referringKeys,_referringValues)=targetContractInstance.referringOf(_address,tokenId);}return(_referringKeys,_referringValues);}/// @notice Get the referred list of an rNFT
// @notice 获取 rNFT 的被引用列表
/// @param tokenId The considered rNFT, _address The corresponding contract address
// @param tokenId 要考虑的 rNFT,_address 对应的合约地址
/// @return The referred mapping of an rNFT
// @return rNFT 的被引用映射
functionreferredOf(address_address,uint256tokenId)externalviewvirtualoverride(IERC_5521,TargetContract)returns(address[]memory,uint256[][]memory){address[]memory_referredKeys;uint256[][]memory_referredValues;if(_address==address(this)){require(_exists(tokenId),"ERC_5521: token ID not existed");// require(_exists(tokenId), "ERC_5521: Token ID 不存在");
(_referredKeys,_referredValues)=convertMap(tokenId,false);}else{TargetContracttargetContractInstance=TargetContract(_address);require(targetContractInstance.supportsInterface(type(TargetContract).interfaceId),"ERC_5521: target contract not supported");// require(targetContractInstance.supportsInterface(type(TargetContract).interfaceId), "ERC_5521: 目标合约不支持");
(_referredKeys,_referredValues)=targetContractInstance.referredOf(_address,tokenId);}return(_referredKeys,_referredValues);}/// @notice Get the timestamp of an rNFT when is being created.
// @notice 获取 rNFT 创建时的时间戳。
/// @param `tokenId` of the rNFT being focused, `_address` of contract address associated with the focused rNFT.
// @param 关注的 rNFT 的 `tokenId`,与关注的 rNFT 关联的合约地址 `_address`。
/// @return The timestamp of the rNFT when is being created with uint256 format.
// @return rNFT 创建时的时间戳,格式为 uint256。
functioncreatedTimestampOf(address_address,uint256tokenId)externalviewreturns(uint256){uint256memorycreatedTimestamp;if(_address==address(this)){require(_exists(tokenId),"ERC_5521: token ID not existed");// require(_exists(tokenId), "ERC_5521: Token ID 不存在");
Relationshipstoragerelationship=_relationship[tokenId];createdTimestamp=relationship.createdTimestamp;}else{TargetContracttargetContractInstance=TargetContract(_address);require(targetContractInstance.supportsInterface(type(TargetContract).interfaceId),"ERC_5521: target contract not supported");// require(targetContractInstance.supportsInterface(type(TargetContract).interfaceId), "ERC_5521: 目标合约不支持");
createdTimestamp=targetContractInstance.createdTimestampOf(_address,tokenId);}returncreatedTimestamp;}/// @dev See {IERC165-supportsInterface}.
// @dev 参见 {IERC165-supportsInterface}。
functionsupportsInterface(bytes4interfaceId)publicviewvirtualoverride(ERC721,IERC_5521,TargetContract)returns(bool){returninterfaceId==type(IERC_5521).interfaceId||interfaceId==type(TargetContract).interfaceId||super.supportsInterface(interfaceId);}// @notice Emit an event of UpdateNode
// @notice 触发一个 UpdateNode 事件
functionemitEvents(uint256tokenId,addresssender)private{(address[]memory_referringKeys,uint256[][]memory_referringValues)=convertMap(tokenId,true);(address[]memory_referredKeys,uint256[][]memory_referredValues)=convertMap(tokenId,false);emitUpdateNode(tokenId,sender,_referringKeys,_referringValues,_referredKeys,_referredValues);}// @notice Convert a specific `local` token mapping to a key array and a value array
// @notice 将特定的“本地”Token 映射转换为键数组和值数组
functionconvertMap(uint256tokenId,boolisReferring)privateviewreturns(address[]memory,uint256[][]memory){Relationshipstoragerelationship=_relationship[tokenId];address[]memoryreturnKeys;uint256[][]memoryreturnValues;if(isReferring){returnKeys=relationship.referringKeys;returnValues=newuint256[][](returnKeys.length);for(uinti=0;i<returnKeys.length;i++){returnValues[i]=relationship.referring[returnKeys[i]];}}else{returnKeys=relationship.referredKeys;returnValues=newuint256[][](returnKeys.length);for(uinti=0;i<returnKeys.length;i++){returnValues[i]=relationship.referred[returnKeys[i]];}}return(returnKeys,returnValues);}}