diff --git a/contracts/RMRK/extension/tokenAttributes/RMRKTokenAttributesRepository.sol b/contracts/RMRK/extension/tokenAttributes/RMRKTokenAttributesRepository.sol index 47161d12..804dadc1 100644 --- a/contracts/RMRK/extension/tokenAttributes/RMRKTokenAttributesRepository.sol +++ b/contracts/RMRK/extension/tokenAttributes/RMRKTokenAttributesRepository.sol @@ -105,6 +105,31 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /// Used to signal that the presigned message's deadline has expired. error ExpiredDeadline(); + /** + * @inheritdoc IERC7508 + */ + function isCollaborator( + address collaborator, + address collection + ) external view returns (bool isCollaborator_) { + isCollaborator_ = _collaborators[collection][collaborator]; + } + + // ------------------- ACCESS CONTROL ------------------- + + /** + * @inheritdoc IERC7508 + */ + function isSpecificAddress( + address specificAddress, + address collection, + string memory key + ) external view returns (bool isSpecificAddress_) { + isSpecificAddress_ = + _parameterSpecificAddress[collection][_keysToIds[key]] == + specificAddress; + } + /** * @inheritdoc IERC7508 */ @@ -185,6 +210,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { } } + // ------------------- METADATA URI ------------------- + /** * @inheritdoc IERC7508 */ @@ -194,285 +221,84 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { attributesMetadataURI = _attributesMetadataURIs[collection]; } - /** - * @inheritdoc IERC7508 - */ - function setAttributesMetadataURIForCollection( - address collection, - string memory attributesMetadataURI - ) external onlyOwner(collection) { - _attributesMetadataURIs[collection] = attributesMetadataURI; - emit MetadataURIUpdated(collection, attributesMetadataURI); - } - - /** - * @inheritdoc IERC7508 - */ - function isCollaborator( - address collaborator, - address collection - ) external view returns (bool isCollaborator_) { - isCollaborator_ = _collaborators[collection][collaborator]; - } - - /** - * @inheritdoc IERC7508 - */ - function isSpecificAddress( - address specificAddress, - address collection, - string memory key - ) external view returns (bool isSpecificAddress_) { - isSpecificAddress_ = - _parameterSpecificAddress[collection][_keysToIds[key]] == - specificAddress; - } - - /** - * @notice Modifier to check if the collection is registered. - * @param collection Address of the collection. - */ - modifier onlyRegisteredCollection(address collection) { - if (!_ownerSettings[collection].registered) { - revert CollectionNotRegistered(); - } - _; - } - - /** - * @notice Modifier to check if the caller is the owner of the collection. - * @param collection Address of the collection. - */ - modifier onlyOwner(address collection) { - if (_ownerSettings[collection].useOwnable) { - if (Ownable(collection).owner() != _msgSender()) { - revert NotCollectionOwner(); - } - } else if (_ownerSettings[collection].owner != _msgSender()) { - revert NotCollectionOwner(); - } - _; - } - - /** - * @notice Function to check if the caller is authorized to mamage a given parameter. - * @param collection The address of the collection. - * @param key Key of the attribute. - * @param tokenId The ID of the token. - */ - function _onlyAuthorizedCaller( - address caller, - address collection, - string memory key, - uint256 tokenId - ) private view { - AccessType accessType = _parameterAccessType[collection][ - _keysToIds[key] - ]; - - if ( - accessType == AccessType.Owner && - ((_ownerSettings[collection].useOwnable && - Ownable(collection).owner() != caller) || - (!_ownerSettings[collection].useOwnable && - _ownerSettings[collection].owner != caller)) - ) { - revert NotCollectionOwner(); - } else if ( - accessType == AccessType.Collaborator && - !_collaborators[collection][caller] - ) { - revert NotCollectionCollaborator(); - } else if ( - accessType == AccessType.OwnerOrCollaborator && - ((_ownerSettings[collection].useOwnable && - Ownable(collection).owner() != caller) || - (!_ownerSettings[collection].useOwnable && - _ownerSettings[collection].owner != caller)) && - !_collaborators[collection][caller] - ) { - revert NotCollectionOwnerOrCollaborator(); - } else if ( - accessType == AccessType.TokenOwner && - IERC721(collection).ownerOf(tokenId) != caller - ) { - revert NotTokenOwner(); - } else if ( - accessType == AccessType.SpecificAddress && - !(_parameterSpecificAddress[collection][_keysToIds[key]] == caller) - ) { - revert NotSpecificAddress(); - } - } + // ------------------- GETTERS ------------------- /** * @inheritdoc IERC7508 */ - function getStringAttribute( + function getAddressAttribute( address collection, uint256 tokenId, string memory key - ) public view returns (string memory attribute) { - attribute = _stringValues[collection][tokenId][_keysToIds[key]]; + ) public view returns (address attribute) { + attribute = _addressValues[collection][tokenId][_keysToIds[key]]; } /** * @inheritdoc IERC7508 */ - function getUintAttribute( + function getBoolAttribute( address collection, uint256 tokenId, string memory key - ) public view returns (uint256 attribute) { - attribute = _uintValues[collection][tokenId][_keysToIds[key]]; + ) public view returns (bool attribute) { + attribute = _boolValues[collection][tokenId][_keysToIds[key]]; } /** * @inheritdoc IERC7508 */ - function getIntAttribute( + function getBytesAttribute( address collection, uint256 tokenId, string memory key - ) public view returns (int256 attribute) { - attribute = _intValues[collection][tokenId][_keysToIds[key]]; + ) public view returns (bytes memory attribute) { + attribute = _bytesValues[collection][tokenId][_keysToIds[key]]; } /** * @inheritdoc IERC7508 */ - function getBoolAttribute( + function getUintAttribute( address collection, uint256 tokenId, string memory key - ) public view returns (bool attribute) { - attribute = _boolValues[collection][tokenId][_keysToIds[key]]; + ) public view returns (uint256 attribute) { + attribute = _uintValues[collection][tokenId][_keysToIds[key]]; } /** * @inheritdoc IERC7508 */ - function getAddressAttribute( + function getStringAttribute( address collection, uint256 tokenId, string memory key - ) public view returns (address attribute) { - attribute = _addressValues[collection][tokenId][_keysToIds[key]]; + ) public view returns (string memory attribute) { + attribute = _stringValues[collection][tokenId][_keysToIds[key]]; } /** * @inheritdoc IERC7508 */ - function getBytesAttribute( + function getIntAttribute( address collection, uint256 tokenId, string memory key - ) public view returns (bytes memory attribute) { - attribute = _bytesValues[collection][tokenId][_keysToIds[key]]; + ) public view returns (int256 attribute) { + attribute = _intValues[collection][tokenId][_keysToIds[key]]; } - /** - * @inheritdoc IERC7508 - */ - function getAttributes( - address collection, - uint256 tokenId, - string[] memory addressKeys, - string[] memory boolKeys, - string[] memory bytesKeys, - string[] memory intKeys, - string[] memory stringKeys, - string[] memory uintKeys - ) - external - view - returns ( - address[] memory addressAttributes, - bool[] memory boolAttributes, - bytes[] memory bytesAttributes, - int256[] memory intAttributes, - string[] memory stringAttributes, - uint256[] memory uintAttributes - ) - { - // WARNING: This implementation is a bit inneficient and differs slightly from the ERC one, to avoid stack too deep errors without using via-IR flag which would affect all other contracts in the package. This is not relevant since you should only need the interface to interact with the actual repo on each network anyway. - stringAttributes = new string[](stringKeys.length); - for (uint256 i; i < stringKeys.length; ) { - stringAttributes[i] = getStringAttribute( - collection, - tokenId, - stringKeys[i] - ); - unchecked { - ++i; - } - } - - uintAttributes = new uint256[](uintKeys.length); - for (uint256 i; i < uintKeys.length; ) { - uintAttributes[i] = getUintAttribute( - collection, - tokenId, - uintKeys[i] - ); - unchecked { - ++i; - } - } - - intAttributes = new int256[](intKeys.length); - for (uint256 i; i < intKeys.length; ) { - intAttributes[i] = getIntAttribute(collection, tokenId, intKeys[i]); - unchecked { - ++i; - } - } - - boolAttributes = new bool[](boolKeys.length); - for (uint256 i; i < boolKeys.length; ) { - boolAttributes[i] = getBoolAttribute( - collection, - tokenId, - boolKeys[i] - ); - unchecked { - ++i; - } - } - - addressAttributes = new address[](addressKeys.length); - for (uint256 i; i < addressKeys.length; ) { - addressAttributes[i] = getAddressAttribute( - collection, - tokenId, - addressKeys[i] - ); - unchecked { - ++i; - } - } - - bytesAttributes = new bytes[](bytesKeys.length); - for (uint256 i; i < bytesKeys.length; ) { - bytesAttributes[i] = getBytesAttribute( - collection, - tokenId, - bytesKeys[i] - ); - unchecked { - ++i; - } - } - } + // ------------------- BATCH GETTERS ------------------- /** * @inheritdoc IERC7508 */ - function getStringAttributes( + function getAddressAttributes( address[] memory collections, uint256[] memory tokenIds, string[] memory attributeKeys - ) public view returns (string[] memory attributes) { + ) public view returns (address[] memory attributes) { ( bool multipleCollections, bool multipleTokens, @@ -484,10 +310,10 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { attributeKeys.length ); - attributes = new string[](loopLength); + attributes = new address[](loopLength); for (uint256 i; i < loopLength; ) { - attributes[i] = getStringAttribute( + attributes[i] = getAddressAttribute( multipleCollections ? collections[i] : collections[0], multipleTokens ? tokenIds[i] : tokenIds[0], multipleAttributes ? attributeKeys[i] : attributeKeys[0] @@ -501,11 +327,11 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /** * @inheritdoc IERC7508 */ - function getUintAttributes( + function getBoolAttributes( address[] memory collections, uint256[] memory tokenIds, string[] memory attributeKeys - ) public view returns (uint256[] memory attributes) { + ) public view returns (bool[] memory attributes) { ( bool multipleCollections, bool multipleTokens, @@ -517,10 +343,10 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { attributeKeys.length ); - attributes = new uint256[](loopLength); + attributes = new bool[](loopLength); for (uint256 i; i < loopLength; ) { - attributes[i] = getUintAttribute( + attributes[i] = getBoolAttribute( multipleCollections ? collections[i] : collections[0], multipleTokens ? tokenIds[i] : tokenIds[0], multipleAttributes ? attributeKeys[i] : attributeKeys[0] @@ -534,11 +360,11 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /** * @inheritdoc IERC7508 */ - function getIntAttributes( + function getBytesAttributes( address[] memory collections, uint256[] memory tokenIds, string[] memory attributeKeys - ) public view returns (int256[] memory attributes) { + ) public view returns (bytes[] memory attributes) { ( bool multipleCollections, bool multipleTokens, @@ -550,10 +376,10 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { attributeKeys.length ); - attributes = new int256[](loopLength); + attributes = new bytes[](loopLength); for (uint256 i; i < loopLength; ) { - attributes[i] = getIntAttribute( + attributes[i] = getBytesAttribute( multipleCollections ? collections[i] : collections[0], multipleTokens ? tokenIds[i] : tokenIds[0], multipleAttributes ? attributeKeys[i] : attributeKeys[0] @@ -567,11 +393,11 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /** * @inheritdoc IERC7508 */ - function getBoolAttributes( + function getIntAttributes( address[] memory collections, uint256[] memory tokenIds, string[] memory attributeKeys - ) public view returns (bool[] memory attributes) { + ) public view returns (int256[] memory attributes) { ( bool multipleCollections, bool multipleTokens, @@ -583,10 +409,10 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { attributeKeys.length ); - attributes = new bool[](loopLength); + attributes = new int256[](loopLength); for (uint256 i; i < loopLength; ) { - attributes[i] = getBoolAttribute( + attributes[i] = getIntAttribute( multipleCollections ? collections[i] : collections[0], multipleTokens ? tokenIds[i] : tokenIds[0], multipleAttributes ? attributeKeys[i] : attributeKeys[0] @@ -600,11 +426,11 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /** * @inheritdoc IERC7508 */ - function getAddressAttributes( + function getStringAttributes( address[] memory collections, uint256[] memory tokenIds, string[] memory attributeKeys - ) public view returns (address[] memory attributes) { + ) public view returns (string[] memory attributes) { ( bool multipleCollections, bool multipleTokens, @@ -616,10 +442,10 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { attributeKeys.length ); - attributes = new address[](loopLength); + attributes = new string[](loopLength); for (uint256 i; i < loopLength; ) { - attributes[i] = getAddressAttribute( + attributes[i] = getStringAttribute( multipleCollections ? collections[i] : collections[0], multipleTokens ? tokenIds[i] : tokenIds[0], multipleAttributes ? attributeKeys[i] : attributeKeys[0] @@ -633,11 +459,11 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /** * @inheritdoc IERC7508 */ - function getBytesAttributes( + function getUintAttributes( address[] memory collections, uint256[] memory tokenIds, string[] memory attributeKeys - ) public view returns (bytes[] memory attributes) { + ) public view returns (uint256[] memory attributes) { ( bool multipleCollections, bool multipleTokens, @@ -649,10 +475,10 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { attributeKeys.length ); - attributes = new bytes[](loopLength); + attributes = new uint256[](loopLength); for (uint256 i; i < loopLength; ) { - attributes[i] = getBytesAttribute( + attributes[i] = getUintAttribute( multipleCollections ? collections[i] : collections[0], multipleTokens ? tokenIds[i] : tokenIds[0], multipleAttributes ? attributeKeys[i] : attributeKeys[0] @@ -663,6 +489,192 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { } } + /** + * @inheritdoc IERC7508 + */ + function getAttributes( + address collection, + uint256 tokenId, + string[] memory addressKeys, + string[] memory boolKeys, + string[] memory bytesKeys, + string[] memory intKeys, + string[] memory stringKeys, + string[] memory uintKeys + ) + external + view + returns ( + address[] memory addressAttributes, + bool[] memory boolAttributes, + bytes[] memory bytesAttributes, + int256[] memory intAttributes, + string[] memory stringAttributes, + uint256[] memory uintAttributes + ) + { + // WARNING: This implementation is a bit inneficient and differs slightly from the ERC one, to avoid stack too deep errors without using via-IR flag which would affect all other contracts in the package. This is not relevant since you should only need the interface to interact with the actual repo on each network anyway. + stringAttributes = new string[](stringKeys.length); + for (uint256 i; i < stringKeys.length; ) { + stringAttributes[i] = getStringAttribute( + collection, + tokenId, + stringKeys[i] + ); + unchecked { + ++i; + } + } + + uintAttributes = new uint256[](uintKeys.length); + for (uint256 i; i < uintKeys.length; ) { + uintAttributes[i] = getUintAttribute( + collection, + tokenId, + uintKeys[i] + ); + unchecked { + ++i; + } + } + + intAttributes = new int256[](intKeys.length); + for (uint256 i; i < intKeys.length; ) { + intAttributes[i] = getIntAttribute(collection, tokenId, intKeys[i]); + unchecked { + ++i; + } + } + + boolAttributes = new bool[](boolKeys.length); + for (uint256 i; i < boolKeys.length; ) { + boolAttributes[i] = getBoolAttribute( + collection, + tokenId, + boolKeys[i] + ); + unchecked { + ++i; + } + } + + addressAttributes = new address[](addressKeys.length); + for (uint256 i; i < addressKeys.length; ) { + addressAttributes[i] = getAddressAttribute( + collection, + tokenId, + addressKeys[i] + ); + unchecked { + ++i; + } + } + + bytesAttributes = new bytes[](bytesKeys.length); + for (uint256 i; i < bytesKeys.length; ) { + bytesAttributes[i] = getBytesAttribute( + collection, + tokenId, + bytesKeys[i] + ); + unchecked { + ++i; + } + } + } + + // ------------------- SETTERS ------------------- + // ------------------- BATCH SETTERS ------------------- + // ------------------- PRESIGNED SETTERS ------------------- + + /** + * @inheritdoc IERC7508 + */ + function setAttributesMetadataURIForCollection( + address collection, + string memory attributesMetadataURI + ) external onlyOwner(collection) { + _attributesMetadataURIs[collection] = attributesMetadataURI; + emit MetadataURIUpdated(collection, attributesMetadataURI); + } + + /** + * @notice Modifier to check if the collection is registered. + * @param collection Address of the collection. + */ + modifier onlyRegisteredCollection(address collection) { + if (!_ownerSettings[collection].registered) { + revert CollectionNotRegistered(); + } + _; + } + + /** + * @notice Modifier to check if the caller is the owner of the collection. + * @param collection Address of the collection. + */ + modifier onlyOwner(address collection) { + if (_ownerSettings[collection].useOwnable) { + if (Ownable(collection).owner() != _msgSender()) { + revert NotCollectionOwner(); + } + } else if (_ownerSettings[collection].owner != _msgSender()) { + revert NotCollectionOwner(); + } + _; + } + + /** + * @notice Function to check if the caller is authorized to mamage a given parameter. + * @param collection The address of the collection. + * @param key Key of the attribute. + * @param tokenId The ID of the token. + */ + function _onlyAuthorizedCaller( + address caller, + address collection, + string memory key, + uint256 tokenId + ) private view { + AccessType accessType = _parameterAccessType[collection][ + _keysToIds[key] + ]; + + if ( + accessType == AccessType.Owner && + ((_ownerSettings[collection].useOwnable && + Ownable(collection).owner() != caller) || + (!_ownerSettings[collection].useOwnable && + _ownerSettings[collection].owner != caller)) + ) { + revert NotCollectionOwner(); + } else if ( + accessType == AccessType.Collaborator && + !_collaborators[collection][caller] + ) { + revert NotCollectionCollaborator(); + } else if ( + accessType == AccessType.OwnerOrCollaborator && + ((_ownerSettings[collection].useOwnable && + Ownable(collection).owner() != caller) || + (!_ownerSettings[collection].useOwnable && + _ownerSettings[collection].owner != caller)) && + !_collaborators[collection][caller] + ) { + revert NotCollectionOwnerOrCollaborator(); + } else if ( + accessType == AccessType.TokenOwner && + IERC721(collection).ownerOf(tokenId) != caller + ) { + revert NotTokenOwner(); + } else if ( + accessType == AccessType.SpecificAddress && + !(_parameterSpecificAddress[collection][_keysToIds[key]] == caller) + ) { + revert NotSpecificAddress(); + } + } + /** * @inheritdoc IERC7508 */