From 4fea571f9869613331f013e70d346f564b6c882a Mon Sep 17 00:00:00 2001 From: steven2308 Date: Fri, 1 Mar 2024 15:14:51 -0500 Subject: [PATCH] Refactor on token attributes to reuse code and allow multiple collections and tokenIds on setters and getters. --- .../extension/tokenAttributes/IERC7508.sol | 100 +-- .../RMRKTokenAttributesRepository.sol | 604 +++++++------ test/extensions/tokenAttributesRepository.ts | 809 ++++++------------ 3 files changed, 661 insertions(+), 852 deletions(-) diff --git a/contracts/RMRK/extension/tokenAttributes/IERC7508.sol b/contracts/RMRK/extension/tokenAttributes/IERC7508.sol index 70fc8a5d..c0aa8735 100644 --- a/contracts/RMRK/extension/tokenAttributes/IERC7508.sol +++ b/contracts/RMRK/extension/tokenAttributes/IERC7508.sol @@ -326,13 +326,13 @@ interface IERC7508 is IERC165 { * string key, * string value * ] - * @param collection Address of the collection - * @param tokenId ID of the token + * @param collections Addresses of the collections, in the same order as the attributes. If all tokens are from the same collection the array can contain a single element with the collection address. + * @param tokenIds IDs of the tokens, in the same order as the attributes. If all attributes are for the same token the array can contain a single element with the token ID. * @param attributes An array of `StringAttribute` structs to be assigned to the given token */ function setStringAttributes( - address collection, - uint256 tokenId, + address[] memory collections, + uint256[] memory tokenIds, StringAttribute[] memory attributes ) external; @@ -343,13 +343,13 @@ interface IERC7508 is IERC165 { * string key, * uint value * ] - * @param collection Address of the collection - * @param tokenId ID of the token + * @param collections Addresses of the collections, in the same order as the attributes. If all tokens are from the same collection the array can contain a single element with the collection address. + * @param tokenIds IDs of the tokens, in the same order as the attributes. If all attributes are for the same token the array can contain a single element with the token ID. * @param attributes An array of `UintAttribute` structs to be assigned to the given token */ function setUintAttributes( - address collection, - uint256 tokenId, + address[] memory collections, + uint256[] memory tokenIds, UintAttribute[] memory attributes ) external; @@ -360,13 +360,13 @@ interface IERC7508 is IERC165 { * string key, * bool value * ] - * @param collection Address of the collection - * @param tokenId ID of the token + * @param collections Addresses of the collections, in the same order as the attributes. If all tokens are from the same collection the array can contain a single element with the collection address. + * @param tokenIds IDs of the tokens, in the same order as the attributes. If all attributes are for the same token the array can contain a single element with the token ID. * @param attributes An array of `BoolAttribute` structs to be assigned to the given token */ function setBoolAttributes( - address collection, - uint256 tokenId, + address[] memory collections, + uint256[] memory tokenIds, BoolAttribute[] memory attributes ) external; @@ -377,13 +377,13 @@ interface IERC7508 is IERC165 { * string key, * address value * ] - * @param collection Address of the collection - * @param tokenId ID of the token + * @param collections Addresses of the collections, in the same order as the attributes. If all tokens are from the same collection the array can contain a single element with the collection address. + * @param tokenIds IDs of the tokens, in the same order as the attributes. If all attributes are for the same token the array can contain a single element with the token ID. * @param attributes An array of `AddressAttribute` structs to be assigned to the given token */ function setAddressAttributes( - address collection, - uint256 tokenId, + address[] memory collections, + uint256[] memory tokenIds, AddressAttribute[] memory attributes ) external; @@ -394,13 +394,13 @@ interface IERC7508 is IERC165 { * string key, * bytes value * ] - * @param collection Address of the collection - * @param tokenId ID of the token + * @param collections Addresses of the collections, in the same order as the attributes. If all tokens are from the same collection the array can contain a single element with the collection address. + * @param tokenIds IDs of the tokens, in the same order as the attributes. If all attributes are for the same token the array can contain a single element with the token ID. * @param attributes An array of `BytesAttribute` structs to be assigned to the given token */ function setBytesAttributes( - address collection, - uint256 tokenId, + address[] memory collections, + uint256[] memory tokenIds, BytesAttribute[] memory attributes ) external; @@ -780,15 +780,15 @@ interface IERC7508 is IERC165 { * string key, * string value * ] - * @param collection Address of the collection the token belongs to - * @param tokenId ID of the token for which the attributes are being retrieved - * @param stringKeys An array of string keys to retrieve + * @param collections Addresses of the collections, in the same order as the attribute keys. If all tokens are from the same collection the array can contain a single element with the collection address. + * @param tokenIds IDs of the tokens, in the same order as the attribute keys. If all attributes are for the same token the array can contain a single element with the token ID. + * @param attributeKeys An array of string keys to retrieve * @return attributes An array of `StringAttribute` structs */ function getStringAttributes( - address collection, - uint256 tokenId, - string[] memory stringKeys + address[] memory collections, + uint256[] memory tokenIds, + string[] memory attributeKeys ) external view returns (StringAttribute[] memory attributes); /** @@ -798,15 +798,15 @@ interface IERC7508 is IERC165 { * string key, * uint value * ] - * @param collection Address of the collection the token belongs to - * @param tokenId ID of the token for which the attributes are being retrieved - * @param uintKeys An array of uint keys to retrieve + * @param collections Addresses of the collections, in the same order as the attribute keys. If all tokens are from the same collection the array can contain a single element with the collection address. + * @param tokenIds IDs of the tokens, in the same order as the attribute keys. If all attributes are for the same token the array can contain a single element with the token ID. + * @param attributeKeys An array of uint keys to retrieve * @return attributes An array of `UintAttribute` structs */ function getUintAttributes( - address collection, - uint256 tokenId, - string[] memory uintKeys + address[] memory collections, + uint256[] memory tokenIds, + string[] memory attributeKeys ) external view returns (UintAttribute[] memory attributes); /** @@ -816,15 +816,15 @@ interface IERC7508 is IERC165 { * string key, * bool value * ] - * @param collection Address of the collection the token belongs to - * @param tokenId ID of the token for which the attributes are being retrieved - * @param boolKeys An array of bool keys to retrieve + * @param collections Addresses of the collections, in the same order as the attribute keys. If all tokens are from the same collection the array can contain a single element with the collection address. + * @param tokenIds IDs of the tokens, in the same order as the attribute keys. If all attributes are for the same token the array can contain a single element with the token ID. + * @param attributeKeys An array of bool keys to retrieve * @return attributes An array of `BoolAttribute` structs */ function getBoolAttributes( - address collection, - uint256 tokenId, - string[] memory boolKeys + address[] memory collections, + uint256[] memory tokenIds, + string[] memory attributeKeys ) external view returns (BoolAttribute[] memory attributes); /** @@ -834,15 +834,15 @@ interface IERC7508 is IERC165 { * string key, * address value * ] - * @param collection Address of the collection the token belongs to - * @param tokenId ID of the token for which the attributes are being retrieved - * @param addressKeys An array of address keys to retrieve + * @param collections Addresses of the collections, in the same order as the attribute keys. If all tokens are from the same collection the array can contain a single element with the collection address. + * @param tokenIds IDs of the tokens, in the same order as the attribute keys. If all attributes are for the same token the array can contain a single element with the token ID. + * @param attributeKeys An array of address keys to retrieve * @return attributes An array of `AddressAttribute` structs */ function getAddressAttributes( - address collection, - uint256 tokenId, - string[] memory addressKeys + address[] memory collections, + uint256[] memory tokenIds, + string[] memory attributeKeys ) external view returns (AddressAttribute[] memory attributes); /** @@ -852,14 +852,14 @@ interface IERC7508 is IERC165 { * string key, * bytes value * ] - * @param collection Address of the collection the token belongs to - * @param tokenId ID of the token for which the attributes are being retrieved - * @param bytesKeys An array of bytes keys to retrieve + * @param collections Addresses of the collections, in the same order as the attribute keys. If all tokens are from the same collection the array can contain a single element with the collection address. + * @param tokenIds IDs of the tokens, in the same order as the attribute keys. If all attributes are for the same token the array can contain a single element with the token ID. + * @param attributeKeys An array of bytes keys to retrieve * @return attributes An array of `BytesAttribute` structs */ function getBytesAttributes( - address collection, - uint256 tokenId, - string[] memory bytesKeys + address[] memory collections, + uint256[] memory tokenIds, + string[] memory attributeKeys ) external view returns (BytesAttribute[] memory attributes); } diff --git a/contracts/RMRK/extension/tokenAttributes/RMRKTokenAttributesRepository.sol b/contracts/RMRK/extension/tokenAttributes/RMRKTokenAttributesRepository.sol index a31bef1f..bf647086 100644 --- a/contracts/RMRK/extension/tokenAttributes/RMRKTokenAttributesRepository.sol +++ b/contracts/RMRK/extension/tokenAttributes/RMRKTokenAttributesRepository.sol @@ -44,35 +44,29 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { "setAddressAttribute(address collection,uint256 tokenId,string memory key,address value)" ); - mapping(address => mapping(uint256 => AccessType)) + mapping(address collection => mapping(uint256 => AccessType)) private _parameterAccessType; - mapping(address => mapping(uint256 => address)) + mapping(address collection => mapping(uint256 => address)) private _parameterSpecificAddress; - mapping(address => IssuerSetting) private _issuerSettings; - mapping(address => mapping(address => bool)) private _collaborators; + mapping(address collection => IssuerSetting) private _issuerSettings; + mapping(address collection => mapping(address collaborator => bool)) + private _collaborators; // For keys, we use a mapping from strings to IDs. // The purpose is to store unique string keys only once, since they are more expensive. mapping(string => uint256) private _keysToIds; uint256 private _totalAttributes; - // For strings, we also use a mapping from strings to IDs, together with a reverse mapping - // The purpose is to store unique string values only once, since they are more expensive, - // and storing only IDs. - mapping(address => uint256) private _totalStringValues; - mapping(address => mapping(string => uint256)) private _stringValueToId; - mapping(address => mapping(uint256 => string)) private _stringIdToValue; - mapping(address => mapping(uint256 => mapping(uint256 => uint256))) - private _stringValueIds; - - mapping(address => mapping(uint256 => mapping(uint256 => address))) + mapping(address collection => mapping(uint256 => mapping(uint256 => address))) private _addressValues; - mapping(address => mapping(uint256 => mapping(uint256 => bytes))) + mapping(address collection => mapping(uint256 => mapping(uint256 => bytes))) private _bytesValues; - mapping(address => mapping(uint256 => mapping(uint256 => uint256))) + mapping(address collection => mapping(uint256 => mapping(uint256 => uint256))) private _uintValues; - mapping(address => mapping(uint256 => mapping(uint256 => bool))) + mapping(address collection => mapping(uint256 => mapping(uint256 => bool))) private _boolValues; + mapping(address collection => mapping(uint256 => mapping(uint256 => string))) + private _stringValues; struct IssuerSetting { bool registered; @@ -80,6 +74,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { address issuer; } + /// Used to signal that the length of the arrays is not equal. + error LengthsMismatch(); /// Used to signal that the smart contract interacting with the repository does not implement Ownable pattern. error OwnableNotImplemented(); /// Used to signal that the caller is not the issuer of the collection. @@ -88,8 +84,6 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { error CollaboratorArraysNotEqualLength(); /// Used to signal that the collection is not registered in the repository yet. error CollectionNotRegistered(); - /// Used to signal that the collection is already registered in the repository. - error CollectionAlreadyRegistered(); /// Used to signal that the caller is not aa collaborator of the collection. error NotCollectionCollaborator(); /// Used to signal that the caller is not the issuer or a collaborator of the collection. @@ -110,7 +104,7 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { address collection, address issuer, bool useOwnable - ) external onlyUnregisteredCollection(collection) { + ) external { (bool ownableSuccess, bytes memory ownableReturn) = collection.call( abi.encodeWithSignature("owner()") ); @@ -125,10 +119,11 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { revert NotCollectionIssuer(); } - IssuerSetting storage issuerSetting = _issuerSettings[collection]; - issuerSetting.registered = true; - issuerSetting.issuer = issuer; - issuerSetting.useOwnable = useOwnable; + _issuerSettings[collection] = IssuerSetting({ + registered: true, + issuer: issuer, + useOwnable: useOwnable + }); emit AccessControlRegistration( collection, @@ -235,17 +230,6 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { _; } - /** - * @notice Modifier to check if the collection is not registered. - * @param collection Address of the collection. - */ - modifier onlyUnregisteredCollection(address collection) { - if (_issuerSettings[collection].registered) { - revert CollectionAlreadyRegistered(); - } - _; - } - /** * @notice Modifier to check if the caller is the issuer of the collection. * @param collection Address of the collection. @@ -319,11 +303,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { address collection, uint256 tokenId, string memory key - ) external view returns (string memory attribute) { - uint256 idForValue = _stringValueIds[collection][tokenId][ - _keysToIds[key] - ]; - attribute = _stringIdToValue[collection][idForValue]; + ) public view returns (string memory attribute) { + attribute = _stringValues[collection][tokenId][_keysToIds[key]]; } /** @@ -333,7 +314,7 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { address collection, uint256 tokenId, string memory key - ) external view returns (uint256 attribute) { + ) public view returns (uint256 attribute) { attribute = _uintValues[collection][tokenId][_keysToIds[key]]; } @@ -344,7 +325,7 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { address collection, uint256 tokenId, string memory key - ) external view returns (bool attribute) { + ) public view returns (bool attribute) { attribute = _boolValues[collection][tokenId][_keysToIds[key]]; } @@ -355,7 +336,7 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { address collection, uint256 tokenId, string memory key - ) external view returns (address attribute) { + ) public view returns (address attribute) { attribute = _addressValues[collection][tokenId][_keysToIds[key]]; } @@ -366,7 +347,7 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { address collection, uint256 tokenId, string memory key - ) external view returns (bytes memory attribute) { + ) public view returns (bytes memory attribute) { attribute = _bytesValues[collection][tokenId][_keysToIds[key]]; } @@ -392,41 +373,50 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { BytesAttribute[] memory bytesAttributes ) { - stringAttributes = getStringAttributes(collection, tokenId, stringKeys); - - uintAttributes = getUintAttributes(collection, tokenId, uintKeys); - - boolAttributes = getBoolAttributes(collection, tokenId, boolKeys); - + address[] memory collections = new address[](1); + uint256[] memory tokenIds = new uint256[](1); + collections[0] = collection; + tokenIds[0] = tokenId; + + stringAttributes = getStringAttributes( + collections, + tokenIds, + stringKeys + ); + uintAttributes = getUintAttributes(collections, tokenIds, uintKeys); + boolAttributes = getBoolAttributes(collections, tokenIds, boolKeys); addressAttributes = getAddressAttributes( - collection, - tokenId, + collections, + tokenIds, addressKeys ); - - bytesAttributes = getBytesAttributes(collection, tokenId, bytesKeys); + bytesAttributes = getBytesAttributes(collections, tokenIds, bytesKeys); } /** * @inheritdoc IERC7508 */ function getStringAttributes( - address collection, - uint256 tokenId, - string[] memory stringKeys + address[] memory collections, + uint256[] memory tokenIds, + string[] memory attributeKeys ) public view returns (StringAttribute[] memory attributes) { - uint256 stringLen = stringKeys.length; + uint256 length = attributeKeys.length; + ( + bool multipleCollections, + bool multipleTokens + ) = _checkIfMultipleCollectionsAndTokens(collections, tokenIds, length); - attributes = new StringAttribute[](stringLen); + attributes = new StringAttribute[](length); - for (uint256 i; i < stringLen; ) { + for (uint256 i; i < length; ) { attributes[i] = StringAttribute({ - key: stringKeys[i], - value: _stringIdToValue[collection][ - _stringValueIds[collection][tokenId][ - _keysToIds[stringKeys[i]] - ] - ] + key: attributeKeys[i], + value: getStringAttribute( + multipleCollections ? collections[i] : collections[0], + multipleTokens ? tokenIds[i] : tokenIds[0], + attributeKeys[i] + ) }); unchecked { ++i; @@ -438,18 +428,26 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { * @inheritdoc IERC7508 */ function getUintAttributes( - address collection, - uint256 tokenId, - string[] memory uintKeys + address[] memory collections, + uint256[] memory tokenIds, + string[] memory attributeKeys ) public view returns (UintAttribute[] memory attributes) { - uint256 uintLen = uintKeys.length; + uint256 length = attributeKeys.length; + ( + bool multipleCollections, + bool multipleTokens + ) = _checkIfMultipleCollectionsAndTokens(collections, tokenIds, length); - attributes = new UintAttribute[](uintLen); + attributes = new UintAttribute[](length); - for (uint256 i; i < uintLen; ) { + for (uint256 i; i < length; ) { attributes[i] = UintAttribute({ - key: uintKeys[i], - value: _uintValues[collection][tokenId][_keysToIds[uintKeys[i]]] + key: attributeKeys[i], + value: getUintAttribute( + multipleCollections ? collections[i] : collections[0], + multipleTokens ? tokenIds[i] : tokenIds[0], + attributeKeys[i] + ) }); unchecked { ++i; @@ -461,18 +459,26 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { * @inheritdoc IERC7508 */ function getBoolAttributes( - address collection, - uint256 tokenId, - string[] memory boolKeys + address[] memory collections, + uint256[] memory tokenIds, + string[] memory attributeKeys ) public view returns (BoolAttribute[] memory attributes) { - uint256 boolLen = boolKeys.length; + uint256 length = attributeKeys.length; + ( + bool multipleCollections, + bool multipleTokens + ) = _checkIfMultipleCollectionsAndTokens(collections, tokenIds, length); - attributes = new BoolAttribute[](boolLen); + attributes = new BoolAttribute[](length); - for (uint256 i; i < boolLen; ) { + for (uint256 i; i < length; ) { attributes[i] = BoolAttribute({ - key: boolKeys[i], - value: _boolValues[collection][tokenId][_keysToIds[boolKeys[i]]] + key: attributeKeys[i], + value: getBoolAttribute( + multipleCollections ? collections[i] : collections[0], + multipleTokens ? tokenIds[i] : tokenIds[0], + attributeKeys[i] + ) }); unchecked { ++i; @@ -484,19 +490,26 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { * @inheritdoc IERC7508 */ function getAddressAttributes( - address collection, - uint256 tokenId, - string[] memory addressKeys + address[] memory collections, + uint256[] memory tokenIds, + string[] memory attributeKeys ) public view returns (AddressAttribute[] memory attributes) { - uint256 addressLen = addressKeys.length; - attributes = new AddressAttribute[](addressLen); + uint256 length = attributeKeys.length; + ( + bool multipleCollections, + bool multipleTokens + ) = _checkIfMultipleCollectionsAndTokens(collections, tokenIds, length); - for (uint256 i; i < addressLen; ) { + attributes = new AddressAttribute[](length); + + for (uint256 i; i < length; ) { attributes[i] = AddressAttribute({ - key: addressKeys[i], - value: _addressValues[collection][tokenId][ - _keysToIds[addressKeys[i]] - ] + key: attributeKeys[i], + value: getAddressAttribute( + multipleCollections ? collections[i] : collections[0], + multipleTokens ? tokenIds[i] : tokenIds[0], + attributeKeys[i] + ) }); unchecked { ++i; @@ -508,19 +521,26 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { * @inheritdoc IERC7508 */ function getBytesAttributes( - address collection, - uint256 tokenId, - string[] memory bytesKeys + address[] memory collections, + uint256[] memory tokenIds, + string[] memory attributeKeys ) public view returns (BytesAttribute[] memory attributes) { - uint256 bytesLen = bytesKeys.length; - attributes = new BytesAttribute[](bytesLen); + uint256 length = attributeKeys.length; + ( + bool multipleCollections, + bool multipleTokens + ) = _checkIfMultipleCollectionsAndTokens(collections, tokenIds, length); + + attributes = new BytesAttribute[](length); - for (uint256 i; i < bytesLen; ) { + for (uint256 i; i < length; ) { attributes[i] = BytesAttribute({ - key: bytesKeys[i], - value: _bytesValues[collection][tokenId][ - _keysToIds[bytesKeys[i]] - ] + key: attributeKeys[i], + value: getBytesAttribute( + multipleCollections ? collections[i] : collections[0], + multipleTokens ? tokenIds[i] : tokenIds[0], + attributeKeys[i] + ) }); unchecked { ++i; @@ -646,84 +666,83 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /** * @inheritdoc IERC7508 */ - function setUintAttribute( + function setBoolAttribute( address collection, uint256 tokenId, string memory key, - uint256 value - ) external onlyAuthorizedCaller(collection, key, tokenId) { - _uintValues[collection][tokenId][_getIdForKey(key)] = value; - emit UintAttributeUpdated(collection, tokenId, key, value); + bool value + ) external { + _setBoolAttribute(_msgSender(), collection, tokenId, key, value); } /** * @inheritdoc IERC7508 */ - function setStringAttribute( + function setBytesAttribute( address collection, uint256 tokenId, string memory key, - string memory value - ) external onlyAuthorizedCaller(collection, key, tokenId) { - _stringValueIds[collection][tokenId][ - _getIdForKey(key) - ] = _getStringIdForValue(collection, value); - emit StringAttributeUpdated(collection, tokenId, key, value); + bytes memory value + ) external { + _setBytesAttribute(_msgSender(), collection, tokenId, key, value); } /** * @inheritdoc IERC7508 */ - function setBoolAttribute( + function setAddressAttribute( address collection, uint256 tokenId, string memory key, - bool value - ) external onlyAuthorizedCaller(collection, key, tokenId) { - _boolValues[collection][tokenId][_getIdForKey(key)] = value; - emit BoolAttributeUpdated(collection, tokenId, key, value); + address value + ) external { + _setAddressAttribute(_msgSender(), collection, tokenId, key, value); } /** * @inheritdoc IERC7508 */ - function setBytesAttribute( + function setUintAttribute( address collection, uint256 tokenId, string memory key, - bytes memory value - ) external onlyAuthorizedCaller(collection, key, tokenId) { - _bytesValues[collection][tokenId][_getIdForKey(key)] = value; - emit BytesAttributeUpdated(collection, tokenId, key, value); + uint256 value + ) external { + _setUintAttribute(_msgSender(), collection, tokenId, key, value); } /** * @inheritdoc IERC7508 */ - function setAddressAttribute( + function setStringAttribute( address collection, uint256 tokenId, string memory key, - address value - ) external onlyAuthorizedCaller(collection, key, tokenId) { - _addressValues[collection][tokenId][_getIdForKey(key)] = value; - emit AddressAttributeUpdated(collection, tokenId, key, value); + string memory value + ) external { + _setStringAttribute(_msgSender(), collection, tokenId, key, value); } /** * @inheritdoc IERC7508 */ - function setStringAttributes( - address collection, - uint256 tokenId, - StringAttribute[] memory attributes - ) external onlyAuthorizedCaller(collection, "", tokenId) { + function setBoolAttributes( + address[] memory collections, + uint256[] memory tokenIds, + BoolAttribute[] memory attributes + ) external { uint256 length = attributes.length; + ( + bool multipleCollections, + bool multipleTokens + ) = _checkIfMultipleCollectionsAndTokens(collections, tokenIds, length); for (uint256 i = 0; i < length; ) { - _stringValueIds[collection][tokenId][ - _getIdForKey(attributes[i].key) - ] = _getStringIdForValue(collection, attributes[i].value); - emit StringAttributeUpdated( + address collection = multipleCollections + ? collections[i] + : collections[0]; + uint256 tokenId = multipleTokens ? tokenIds[i] : tokenIds[0]; + _setBoolAttribute( + _msgSender(), collection, tokenId, attributes[i].key, @@ -738,17 +757,23 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /** * @inheritdoc IERC7508 */ - function setUintAttributes( - address collection, - uint256 tokenId, - UintAttribute[] memory attributes - ) external onlyAuthorizedCaller(collection, "", tokenId) { + function setBytesAttributes( + address[] memory collections, + uint256[] memory tokenIds, + BytesAttribute[] memory attributes + ) external { uint256 length = attributes.length; + ( + bool multipleCollections, + bool multipleTokens + ) = _checkIfMultipleCollectionsAndTokens(collections, tokenIds, length); for (uint256 i = 0; i < length; ) { - _uintValues[collection][tokenId][ - _getIdForKey(attributes[i].key) - ] = attributes[i].value; - emit UintAttributeUpdated( + address collection = multipleCollections + ? collections[i] + : collections[0]; + uint256 tokenId = multipleTokens ? tokenIds[i] : tokenIds[0]; + _setBytesAttribute( + _msgSender(), collection, tokenId, attributes[i].key, @@ -763,17 +788,23 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /** * @inheritdoc IERC7508 */ - function setBoolAttributes( - address collection, - uint256 tokenId, - BoolAttribute[] memory attributes - ) external onlyAuthorizedCaller(collection, "", tokenId) { + function setStringAttributes( + address[] memory collections, + uint256[] memory tokenIds, + StringAttribute[] memory attributes + ) external { uint256 length = attributes.length; + ( + bool multipleCollections, + bool multipleTokens + ) = _checkIfMultipleCollectionsAndTokens(collections, tokenIds, length); for (uint256 i = 0; i < length; ) { - _boolValues[collection][tokenId][ - _getIdForKey(attributes[i].key) - ] = attributes[i].value; - emit BoolAttributeUpdated( + address collection = multipleCollections + ? collections[i] + : collections[0]; + uint256 tokenId = multipleTokens ? tokenIds[i] : tokenIds[0]; + _setStringAttribute( + _msgSender(), collection, tokenId, attributes[i].key, @@ -788,17 +819,23 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /** * @inheritdoc IERC7508 */ - function setAddressAttributes( - address collection, - uint256 tokenId, - AddressAttribute[] memory attributes - ) external onlyAuthorizedCaller(collection, "", tokenId) { + function setUintAttributes( + address[] memory collections, + uint256[] memory tokenIds, + UintAttribute[] memory attributes + ) external { uint256 length = attributes.length; + ( + bool multipleCollections, + bool multipleTokens + ) = _checkIfMultipleCollectionsAndTokens(collections, tokenIds, length); for (uint256 i = 0; i < length; ) { - _addressValues[collection][tokenId][ - _getIdForKey(attributes[i].key) - ] = attributes[i].value; - emit AddressAttributeUpdated( + address collection = multipleCollections + ? collections[i] + : collections[0]; + uint256 tokenId = multipleTokens ? tokenIds[i] : tokenIds[0]; + _setUintAttribute( + _msgSender(), collection, tokenId, attributes[i].key, @@ -813,17 +850,23 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { /** * @inheritdoc IERC7508 */ - function setBytesAttributes( - address collection, - uint256 tokenId, - BytesAttribute[] memory attributes - ) external onlyAuthorizedCaller(collection, "", tokenId) { + function setAddressAttributes( + address[] memory collections, + uint256[] memory tokenIds, + AddressAttribute[] memory attributes + ) external { uint256 length = attributes.length; + ( + bool multipleCollections, + bool multipleTokens + ) = _checkIfMultipleCollectionsAndTokens(collections, tokenIds, length); for (uint256 i = 0; i < length; ) { - _bytesValues[collection][tokenId][ - _getIdForKey(attributes[i].key) - ] = attributes[i].value; - emit BytesAttributeUpdated( + address collection = multipleCollections + ? collections[i] + : collections[0]; + uint256 tokenId = multipleTokens ? tokenIds[i] : tokenIds[0]; + _setAddressAttribute( + _msgSender(), collection, tokenId, attributes[i].key, @@ -846,13 +889,11 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { BoolAttribute[] memory boolAttributes, AddressAttribute[] memory addressAttributes, BytesAttribute[] memory bytesAttributes - ) external onlyAuthorizedCaller(collection, "", tokenId) { + ) external { uint256 length = stringAttributes.length; for (uint256 i = 0; i < length; ) { - _stringValueIds[collection][tokenId][ - _getIdForKey(stringAttributes[i].key) - ] = _getStringIdForValue(collection, stringAttributes[i].value); - emit StringAttributeUpdated( + _setStringAttribute( + _msgSender(), collection, tokenId, stringAttributes[i].key, @@ -865,10 +906,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { length = uintAttributes.length; for (uint256 i = 0; i < length; ) { - _uintValues[collection][tokenId][ - _getIdForKey(uintAttributes[i].key) - ] = uintAttributes[i].value; - emit UintAttributeUpdated( + _setUintAttribute( + _msgSender(), collection, tokenId, uintAttributes[i].key, @@ -881,10 +920,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { length = boolAttributes.length; for (uint256 i = 0; i < length; ) { - _boolValues[collection][tokenId][ - _getIdForKey(boolAttributes[i].key) - ] = boolAttributes[i].value; - emit BoolAttributeUpdated( + _setBoolAttribute( + _msgSender(), collection, tokenId, boolAttributes[i].key, @@ -897,10 +934,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { length = addressAttributes.length; for (uint256 i = 0; i < length; ) { - _addressValues[collection][tokenId][ - _getIdForKey(addressAttributes[i].key) - ] = addressAttributes[i].value; - emit AddressAttributeUpdated( + _setAddressAttribute( + _msgSender(), collection, tokenId, addressAttributes[i].key, @@ -913,10 +948,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { length = bytesAttributes.length; for (uint256 i = 0; i < length; ) { - _bytesValues[collection][tokenId][ - _getIdForKey(bytesAttributes[i].key) - ] = bytesAttributes[i].value; - emit BytesAttributeUpdated( + _setBytesAttribute( + _msgSender(), collection, tokenId, bytesAttributes[i].key, @@ -928,6 +961,81 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { } } + function _checkIfMultipleCollectionsAndTokens( + address[] memory collections, + uint256[] memory tokenIds, + uint256 attributesLength + ) internal pure returns (bool multipleCollections, bool multipleTokens) { + multipleCollections = collections.length != 1; + multipleTokens = tokenIds.length != 1; + if ( + (multipleCollections && collections.length != attributesLength) || + (multipleTokens && tokenIds.length != attributesLength) + ) { + revert LengthsMismatch(); + } + } + + function _setBoolAttribute( + address caller, + address collection, + uint256 tokenId, + string memory key, + bool value + ) internal { + _onlyAuthorizedCaller(caller, collection, key, tokenId); + _boolValues[collection][tokenId][_getIdForKey(key)] = value; + emit BoolAttributeUpdated(collection, tokenId, key, value); + } + + function _setBytesAttribute( + address caller, + address collection, + uint256 tokenId, + string memory key, + bytes memory value + ) internal { + _onlyAuthorizedCaller(caller, collection, key, tokenId); + _bytesValues[collection][tokenId][_getIdForKey(key)] = value; + emit BytesAttributeUpdated(collection, tokenId, key, value); + } + + function _setAddressAttribute( + address caller, + address collection, + uint256 tokenId, + string memory key, + address value + ) internal { + _onlyAuthorizedCaller(caller, collection, key, tokenId); + _addressValues[collection][tokenId][_getIdForKey(key)] = value; + emit AddressAttributeUpdated(collection, tokenId, key, value); + } + + function _setStringAttribute( + address caller, + address collection, + uint256 tokenId, + string memory key, + string memory value + ) internal { + _onlyAuthorizedCaller(caller, collection, key, tokenId); + _stringValues[collection][tokenId][_getIdForKey(key)] = value; + emit StringAttributeUpdated(collection, tokenId, key, value); + } + + function _setUintAttribute( + address caller, + address collection, + uint256 tokenId, + string memory key, + uint256 value + ) internal { + _onlyAuthorizedCaller(caller, collection, key, tokenId); + _uintValues[collection][tokenId][_getIdForKey(key)] = value; + emit UintAttributeUpdated(collection, tokenId, key, value); + } + /** * @inheritdoc IERC7508 */ @@ -942,10 +1050,6 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { bytes32 r, bytes32 s ) external { - if (block.timestamp > deadline) { - revert ExpiredDeadline(); - } - bytes32 digest = keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", @@ -962,14 +1066,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { ) ) ); - address signer = ecrecover(digest, v, r, s); - if (signer != setter) { - revert InvalidSignature(); - } - _onlyAuthorizedCaller(signer, collection, key, tokenId); - - _uintValues[collection][tokenId][_getIdForKey(key)] = value; - emit UintAttributeUpdated(collection, tokenId, key, value); + _checkDeadlineAndSigner(setter, deadline, digest, v, r, s); + _setUintAttribute(setter, collection, tokenId, key, value); } /** @@ -986,10 +1084,6 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { bytes32 r, bytes32 s ) external { - if (block.timestamp > deadline) { - revert ExpiredDeadline(); - } - bytes32 digest = keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", @@ -1006,16 +1100,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { ) ) ); - address signer = ecrecover(digest, v, r, s); - if (signer != setter) { - revert InvalidSignature(); - } - _onlyAuthorizedCaller(signer, collection, key, tokenId); - - _stringValueIds[collection][tokenId][ - _getIdForKey(key) - ] = _getStringIdForValue(collection, value); - emit StringAttributeUpdated(collection, tokenId, key, value); + _checkDeadlineAndSigner(setter, deadline, digest, v, r, s); + _setStringAttribute(setter, collection, tokenId, key, value); } /** @@ -1032,10 +1118,6 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { bytes32 r, bytes32 s ) external { - if (block.timestamp > deadline) { - revert ExpiredDeadline(); - } - bytes32 digest = keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", @@ -1052,14 +1134,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { ) ) ); - address signer = ecrecover(digest, v, r, s); - if (signer != setter) { - revert InvalidSignature(); - } - _onlyAuthorizedCaller(signer, collection, key, tokenId); - - _boolValues[collection][tokenId][_getIdForKey(key)] = value; - emit BoolAttributeUpdated(collection, tokenId, key, value); + _checkDeadlineAndSigner(setter, deadline, digest, v, r, s); + _setBoolAttribute(setter, collection, tokenId, key, value); } /** @@ -1076,10 +1152,6 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { bytes32 r, bytes32 s ) external { - if (block.timestamp > deadline) { - revert ExpiredDeadline(); - } - bytes32 digest = keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", @@ -1096,14 +1168,8 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { ) ) ); - address signer = ecrecover(digest, v, r, s); - if (signer != setter) { - revert InvalidSignature(); - } - _onlyAuthorizedCaller(signer, collection, key, tokenId); - - _bytesValues[collection][tokenId][_getIdForKey(key)] = value; - emit BytesAttributeUpdated(collection, tokenId, key, value); + _checkDeadlineAndSigner(setter, deadline, digest, v, r, s); + _setBytesAttribute(setter, collection, tokenId, key, value); } /** @@ -1120,10 +1186,6 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { bytes32 r, bytes32 s ) external { - if (block.timestamp > deadline) { - revert ExpiredDeadline(); - } - bytes32 digest = keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", @@ -1140,14 +1202,25 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { ) ) ); + _checkDeadlineAndSigner(setter, deadline, digest, v, r, s); + _setAddressAttribute(setter, collection, tokenId, key, value); + } + + function _checkDeadlineAndSigner( + address setter, + uint256 deadline, + bytes32 digest, + uint8 v, + bytes32 r, + bytes32 s + ) internal view { + if (block.timestamp > deadline) { + revert ExpiredDeadline(); + } address signer = ecrecover(digest, v, r, s); if (signer != setter) { revert InvalidSignature(); } - _onlyAuthorizedCaller(signer, collection, key, tokenId); - - _addressValues[collection][tokenId][_getIdForKey(key)] = value; - emit AddressAttributeUpdated(collection, tokenId, key, value); } /** @@ -1167,31 +1240,6 @@ contract RMRKTokenAttributesRepository is IERC7508, Context { } } - /** - * @notice Used to get the ID for a string value. If the value does not exist, a new ID is created. - * @dev IDs are shared among all tokens and used only for strings. - * @param collection Address of the collection being checked for string ID - * @param value The attribute value - * @return stringId The id for the string value - */ - function _getStringIdForValue( - address collection, - string memory value - ) internal returns (uint256 stringId) { - if (_stringValueToId[collection][value] == 0) { - _totalStringValues[collection]++; - _stringValueToId[collection][value] = _totalStringValues[ - collection - ]; - _stringIdToValue[collection][ - _totalStringValues[collection] - ] = value; - stringId = _totalStringValues[collection]; - } else { - stringId = _stringValueToId[collection][value]; - } - } - /** * @inheritdoc IERC165 */ diff --git a/test/extensions/tokenAttributesRepository.ts b/test/extensions/tokenAttributesRepository.ts index 7af82d34..76f89c74 100644 --- a/test/extensions/tokenAttributesRepository.ts +++ b/test/extensions/tokenAttributesRepository.ts @@ -40,12 +40,14 @@ describe('RMRKTokenAttributesRepository', async function () { let collectionOwner: SignerWithAddress; let tokenOwner: SignerWithAddress; let collaborator: SignerWithAddress; + let collectionAddress: string; const tokenId = 1n; const tokenId2 = 2n; beforeEach(async function () { tokenAttributes = await loadFixture(tokenAttributesFixture); ownedCollection = await loadFixture(ownedCollectionFixture); + collectionAddress = await ownedCollection.getAddress(); [collectionOwner, tokenOwner, collaborator] = await ethers.getSigners(); this.tokenAttributes = tokenAttributes; @@ -57,7 +59,7 @@ describe('RMRKTokenAttributesRepository', async function () { describe('Registering attributes and setting values', async function () { beforeEach(async function () { await tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), false, ); @@ -66,156 +68,90 @@ describe('RMRKTokenAttributesRepository', async function () { it('can set and get token attributes', async function () { expect( await tokenAttributes.setStringAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'description', 'test description', ), ) .to.emit(tokenAttributes, 'StringAttributeSet') - .withArgs(await ownedCollection.getAddress(), tokenId, 'description', 'test description'); + .withArgs(collectionAddress, tokenId, 'description', 'test description'); expect( await tokenAttributes.setStringAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'description1', 'test description', ), ) .to.emit(tokenAttributes, 'StringAttributeSet') - .withArgs(await ownedCollection.getAddress(), tokenId, 'description1', 'test description'); - expect( - await tokenAttributes.setBoolAttribute( - await ownedCollection.getAddress(), - tokenId, - 'rare', - true, - ), - ) + .withArgs(collectionAddress, tokenId, 'description1', 'test description'); + expect(await tokenAttributes.setBoolAttribute(collectionAddress, tokenId, 'rare', true)) .to.emit(tokenAttributes, 'BoolAttributeSet') - .withArgs(await ownedCollection.getAddress(), tokenId, 'rare', true); + .withArgs(collectionAddress, tokenId, 'rare', true); expect( await tokenAttributes.setAddressAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'owner', tokenOwner.address, ), ) .to.emit(tokenAttributes, 'AddressAttributeSet') - .withArgs(await ownedCollection.getAddress(), tokenId, 'owner', tokenOwner.address); - expect( - await tokenAttributes.setUintAttribute( - await ownedCollection.getAddress(), - tokenId, - 'atk', - 100n, - ), - ) + .withArgs(collectionAddress, tokenId, 'owner', tokenOwner.address); + expect(await tokenAttributes.setUintAttribute(collectionAddress, tokenId, 'atk', 100n)) .to.emit(tokenAttributes, 'UintAttributeSet') - .withArgs(await ownedCollection.getAddress(), tokenId, 'atk', 100n); - expect( - await tokenAttributes.setUintAttribute( - await ownedCollection.getAddress(), - tokenId, - 'health', - 100n, - ), - ) + .withArgs(collectionAddress, tokenId, 'atk', 100n); + expect(await tokenAttributes.setUintAttribute(collectionAddress, tokenId, 'health', 100n)) .to.emit(tokenAttributes, 'UintAttributeSet') - .withArgs(await ownedCollection.getAddress(), tokenId, 'health', 100n); - expect( - await tokenAttributes.setUintAttribute( - await ownedCollection.getAddress(), - tokenId, - 'health', - 95n, - ), - ) + .withArgs(collectionAddress, tokenId, 'health', 100n); + expect(await tokenAttributes.setUintAttribute(collectionAddress, tokenId, 'health', 95n)) .to.emit(tokenAttributes, 'UintAttributeSet') - .withArgs(await ownedCollection.getAddress(), tokenId, 'health', 95n); - expect( - await tokenAttributes.setUintAttribute( - await ownedCollection.getAddress(), - tokenId, - 'health', - 80n, - ), - ) + .withArgs(collectionAddress, tokenId, 'health', 95n); + expect(await tokenAttributes.setUintAttribute(collectionAddress, tokenId, 'health', 80n)) .to.emit(tokenAttributes, 'UintAttributeSet') - .withArgs(await ownedCollection.getAddress(), tokenId, 'health', 80n); - expect( - await tokenAttributes.setBytesAttribute( - await ownedCollection.getAddress(), - tokenId, - 'data', - '0x1234', - ), - ) + .withArgs(collectionAddress, tokenId, 'health', 80n); + expect(await tokenAttributes.setBytesAttribute(collectionAddress, tokenId, 'data', '0x1234')) .to.emit(tokenAttributes, 'BytesAttributeSet') - .withArgs(await ownedCollection.getAddress(), tokenId, 'data', '0x1234'); + .withArgs(collectionAddress, tokenId, 'data', '0x1234'); expect( - await tokenAttributes.getStringAttribute( - await ownedCollection.getAddress(), - tokenId, - 'description', - ), + await tokenAttributes.getStringAttribute(collectionAddress, tokenId, 'description'), ).to.eql('test description'); expect( - await tokenAttributes.getStringAttribute( - await ownedCollection.getAddress(), - tokenId, - 'description1', - ), + await tokenAttributes.getStringAttribute(collectionAddress, tokenId, 'description1'), ).to.eql('test description'); - expect( - await tokenAttributes.getBoolAttribute(await ownedCollection.getAddress(), tokenId, 'rare'), - ).to.eql(true); - expect( - await tokenAttributes.getAddressAttribute( - await ownedCollection.getAddress(), - tokenId, - 'owner', - ), - ).to.eql(tokenOwner.address); - expect( - await tokenAttributes.getUintAttribute(await ownedCollection.getAddress(), tokenId, 'atk'), - ).to.eql(100n); - expect( - await tokenAttributes.getUintAttribute( - await ownedCollection.getAddress(), - tokenId, - 'health', - ), - ).to.eql(80n); - expect( - await tokenAttributes.getBytesAttribute( - await ownedCollection.getAddress(), - tokenId, - 'data', - ), - ).to.eql('0x1234'); + expect(await tokenAttributes.getBoolAttribute(collectionAddress, tokenId, 'rare')).to.eql( + true, + ); + expect(await tokenAttributes.getAddressAttribute(collectionAddress, tokenId, 'owner')).to.eql( + tokenOwner.address, + ); + expect(await tokenAttributes.getUintAttribute(collectionAddress, tokenId, 'atk')).to.eql( + 100n, + ); + expect(await tokenAttributes.getUintAttribute(collectionAddress, tokenId, 'health')).to.eql( + 80n, + ); + expect(await tokenAttributes.getBytesAttribute(collectionAddress, tokenId, 'data')).to.eql( + '0x1234', + ); await tokenAttributes.setStringAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'description', 'test description update', ); expect( - await tokenAttributes.getStringAttribute( - await ownedCollection.getAddress(), - tokenId, - 'description', - ), + await tokenAttributes.getStringAttribute(collectionAddress, tokenId, 'description'), ).to.eql('test description update'); }); it('can set multiple attributes of multiple types at the same time', async function () { await expect( tokenAttributes.setAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [ { key: 'string1', value: 'value1' }, @@ -240,35 +176,30 @@ describe('RMRKTokenAttributesRepository', async function () { ), ) .to.emit(tokenAttributes, 'StringAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'string1', 'value1') + .withArgs(collectionAddress, tokenId, 'string1', 'value1') .to.emit(tokenAttributes, 'StringAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'string2', 'value2') + .withArgs(collectionAddress, tokenId, 'string2', 'value2') .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'uint1', 1n) + .withArgs(collectionAddress, tokenId, 'uint1', 1n) .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'uint2', 2n) + .withArgs(collectionAddress, tokenId, 'uint2', 2n) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool1', true) + .withArgs(collectionAddress, tokenId, 'bool1', true) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool2', false) + .withArgs(collectionAddress, tokenId, 'bool2', false) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'address1', tokenOwner.address) + .withArgs(collectionAddress, tokenId, 'address1', tokenOwner.address) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs( - await ownedCollection.getAddress(), - tokenId, - 'address2', - await collectionOwner.getAddress(), - ) + .withArgs(collectionAddress, tokenId, 'address2', await collectionOwner.getAddress()) .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bytes1', '0x1234') + .withArgs(collectionAddress, tokenId, 'bytes1', '0x1234') .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bytes2', '0x5678'); + .withArgs(collectionAddress, tokenId, 'bytes2', '0x5678'); }); it('can update multiple attributes of multiple types at the same time', async function () { await tokenAttributes.setAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [ { key: 'string1', value: 'value0' }, @@ -294,7 +225,7 @@ describe('RMRKTokenAttributesRepository', async function () { await expect( tokenAttributes.setAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [ { key: 'string1', value: 'value1' }, @@ -319,35 +250,30 @@ describe('RMRKTokenAttributesRepository', async function () { ), ) .to.emit(tokenAttributes, 'StringAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'string1', 'value1') + .withArgs(collectionAddress, tokenId, 'string1', 'value1') .to.emit(tokenAttributes, 'StringAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'string2', 'value2') + .withArgs(collectionAddress, tokenId, 'string2', 'value2') .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'uint1', 1n) + .withArgs(collectionAddress, tokenId, 'uint1', 1n) .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'uint2', 2n) + .withArgs(collectionAddress, tokenId, 'uint2', 2n) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool1', true) + .withArgs(collectionAddress, tokenId, 'bool1', true) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool2', false) + .withArgs(collectionAddress, tokenId, 'bool2', false) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'address1', tokenOwner.address) + .withArgs(collectionAddress, tokenId, 'address1', tokenOwner.address) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs( - await ownedCollection.getAddress(), - tokenId, - 'address2', - await collectionOwner.getAddress(), - ) + .withArgs(collectionAddress, tokenId, 'address2', await collectionOwner.getAddress()) .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bytes1', '0x1234') + .withArgs(collectionAddress, tokenId, 'bytes1', '0x1234') .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bytes2', '0x5678'); + .withArgs(collectionAddress, tokenId, 'bytes2', '0x5678'); }); it('can set and update multiple attributes of multiple types at the same time even if not all types are updated at the same time', async function () { await tokenAttributes.setAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [{ key: 'string1', value: 'value0' }], [ @@ -367,7 +293,7 @@ describe('RMRKTokenAttributesRepository', async function () { await expect( tokenAttributes.setAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [], [ @@ -389,30 +315,25 @@ describe('RMRKTokenAttributesRepository', async function () { ), ) .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'uint1', 1n) + .withArgs(collectionAddress, tokenId, 'uint1', 1n) .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'uint2', 2n) + .withArgs(collectionAddress, tokenId, 'uint2', 2n) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool1', true) + .withArgs(collectionAddress, tokenId, 'bool1', true) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool2', false) + .withArgs(collectionAddress, tokenId, 'bool2', false) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'address1', tokenOwner.address) + .withArgs(collectionAddress, tokenId, 'address1', tokenOwner.address) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs( - await ownedCollection.getAddress(), - tokenId, - 'address2', - await collectionOwner.getAddress(), - ) + .withArgs(collectionAddress, tokenId, 'address2', await collectionOwner.getAddress()) .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bytes1', '0x1234') + .withArgs(collectionAddress, tokenId, 'bytes1', '0x1234') .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bytes2', '0x5678'); + .withArgs(collectionAddress, tokenId, 'bytes2', '0x5678'); await expect( tokenAttributes.setAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [], [], @@ -425,15 +346,15 @@ describe('RMRKTokenAttributesRepository', async function () { ), ) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool1', false) + .withArgs(collectionAddress, tokenId, 'bool1', false) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool2', true); + .withArgs(collectionAddress, tokenId, 'bool2', true); }); it('can set and update multiple attributes of multiple types at the same time', async function () { await expect( tokenAttributes.setAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [ { key: 'string1', value: 'value1' }, @@ -458,35 +379,30 @@ describe('RMRKTokenAttributesRepository', async function () { ), ) .to.emit(tokenAttributes, 'StringAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'string1', 'value1') + .withArgs(collectionAddress, tokenId, 'string1', 'value1') .to.emit(tokenAttributes, 'StringAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'string2', 'value2') + .withArgs(collectionAddress, tokenId, 'string2', 'value2') .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'uint1', 1n) + .withArgs(collectionAddress, tokenId, 'uint1', 1n) .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'uint2', 2n) + .withArgs(collectionAddress, tokenId, 'uint2', 2n) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool1', true) + .withArgs(collectionAddress, tokenId, 'bool1', true) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool2', false) + .withArgs(collectionAddress, tokenId, 'bool2', false) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'address1', tokenOwner.address) + .withArgs(collectionAddress, tokenId, 'address1', tokenOwner.address) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs( - await ownedCollection.getAddress(), - tokenId, - 'address2', - await collectionOwner.getAddress(), - ) + .withArgs(collectionAddress, tokenId, 'address2', await collectionOwner.getAddress()) .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bytes1', '0x1234') + .withArgs(collectionAddress, tokenId, 'bytes1', '0x1234') .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bytes2', '0x5678'); + .withArgs(collectionAddress, tokenId, 'bytes2', '0x5678'); }); it('should allow to retrieve multiple attributes at once', async function () { await tokenAttributes.setAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [ { key: 'string1', value: 'value1' }, @@ -512,7 +428,7 @@ describe('RMRKTokenAttributesRepository', async function () { expect( await tokenAttributes.getAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, ['string1', 'string2'], ['uint1', 'uint2'], @@ -546,19 +462,23 @@ describe('RMRKTokenAttributesRepository', async function () { it('can set multiple string attributes at the same time', async function () { await expect( - tokenAttributes.setStringAttributes(await ownedCollection.getAddress(), tokenId, [ - { key: 'string1', value: 'value1' }, - { key: 'string2', value: 'value2' }, - ]), + tokenAttributes.setStringAttributes( + [collectionAddress], + [tokenId], + [ + { key: 'string1', value: 'value1' }, + { key: 'string2', value: 'value2' }, + ], + ), ) .to.emit(tokenAttributes, 'StringAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'string1', 'value1') + .withArgs(collectionAddress, tokenId, 'string1', 'value1') .to.emit(tokenAttributes, 'StringAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'string2', 'value2'); + .withArgs(collectionAddress, tokenId, 'string2', 'value2'); expect( await tokenAttributes.getAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, ['string1', 'string2'], [], @@ -580,19 +500,23 @@ describe('RMRKTokenAttributesRepository', async function () { it('can set multiple uint attributes at the same time', async function () { await expect( - tokenAttributes.setUintAttributes(await ownedCollection.getAddress(), tokenId, [ - { key: 'uint1', value: 1n }, - { key: 'uint2', value: 2n }, - ]), + tokenAttributes.setUintAttributes( + [collectionAddress], + [tokenId], + [ + { key: 'uint1', value: 1n }, + { key: 'uint2', value: 2n }, + ], + ), ) .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'uint1', 1n) + .withArgs(collectionAddress, tokenId, 'uint1', 1n) .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'uint2', 2n); + .withArgs(collectionAddress, tokenId, 'uint2', 2n); expect( await tokenAttributes.getAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [], ['uint1', 'uint2'], @@ -614,19 +538,23 @@ describe('RMRKTokenAttributesRepository', async function () { it('can set multiple bool attributes at the same time', async function () { await expect( - tokenAttributes.setBoolAttributes(await ownedCollection.getAddress(), tokenId, [ - { key: 'bool1', value: true }, - { key: 'bool2', value: false }, - ]), + tokenAttributes.setBoolAttributes( + [collectionAddress], + [tokenId], + [ + { key: 'bool1', value: true }, + { key: 'bool2', value: false }, + ], + ), ) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool1', true) + .withArgs(collectionAddress, tokenId, 'bool1', true) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bool2', false); + .withArgs(collectionAddress, tokenId, 'bool2', false); expect( await tokenAttributes.getAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [], [], @@ -648,24 +576,23 @@ describe('RMRKTokenAttributesRepository', async function () { it('can set multiple address attributes at the same time', async function () { await expect( - tokenAttributes.setAddressAttributes(await ownedCollection.getAddress(), tokenId, [ - { key: 'address1', value: tokenOwner.address }, - { key: 'address2', value: await collectionOwner.getAddress() }, - ]), + tokenAttributes.setAddressAttributes( + [collectionAddress], + [tokenId], + [ + { key: 'address1', value: tokenOwner.address }, + { key: 'address2', value: await collectionOwner.getAddress() }, + ], + ), ) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'address1', tokenOwner.address) + .withArgs(collectionAddress, tokenId, 'address1', tokenOwner.address) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs( - await ownedCollection.getAddress(), - tokenId, - 'address2', - await collectionOwner.getAddress(), - ); + .withArgs(collectionAddress, tokenId, 'address2', await collectionOwner.getAddress()); expect( await tokenAttributes.getAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [], [], @@ -687,19 +614,23 @@ describe('RMRKTokenAttributesRepository', async function () { it('can set multiple bytes attributes at the same time', async function () { await expect( - tokenAttributes.setBytesAttributes(await ownedCollection.getAddress(), tokenId, [ - { key: 'bytes1', value: '0x1234' }, - { key: 'bytes2', value: '0x5678' }, - ]), + tokenAttributes.setBytesAttributes( + [collectionAddress], + [tokenId], + [ + { key: 'bytes1', value: '0x1234' }, + { key: 'bytes2', value: '0x5678' }, + ], + ), ) .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bytes1', '0x1234') + .withArgs(collectionAddress, tokenId, 'bytes1', '0x1234') .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), tokenId, 'bytes2', '0x5678'); + .withArgs(collectionAddress, tokenId, 'bytes2', '0x5678'); expect( await tokenAttributes.getAttributes( - await ownedCollection.getAddress(), + collectionAddress, tokenId, [], [], @@ -720,119 +651,71 @@ describe('RMRKTokenAttributesRepository', async function () { }); it('can reuse keys and values are fine', async function () { - await tokenAttributes.setStringAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', + await tokenAttributes.setStringAttribute(collectionAddress, tokenId, 'X', 'X1'); + await tokenAttributes.setStringAttribute(collectionAddress, tokenId2, 'X', 'X2'); + + expect(await tokenAttributes.getStringAttribute(collectionAddress, tokenId, 'X')).to.eql( 'X1', ); - await tokenAttributes.setStringAttribute( - await ownedCollection.getAddress(), - tokenId2, - 'X', + expect(await tokenAttributes.getStringAttribute(collectionAddress, tokenId2, 'X')).to.eql( 'X2', ); - - expect( - await tokenAttributes.getStringAttribute(await ownedCollection.getAddress(), tokenId, 'X'), - ).to.eql('X1'); - expect( - await tokenAttributes.getStringAttribute(await ownedCollection.getAddress(), tokenId2, 'X'), - ).to.eql('X2'); }); it('can reuse keys among different attributes and values are fine', async function () { - await tokenAttributes.setStringAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - 'test description', - ); - await tokenAttributes.setBoolAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - true, - ); + await tokenAttributes.setStringAttribute(collectionAddress, tokenId, 'X', 'test description'); + await tokenAttributes.setBoolAttribute(collectionAddress, tokenId, 'X', true); await tokenAttributes.setAddressAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', tokenOwner.address, ); - await tokenAttributes.setUintAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - 100n, + await tokenAttributes.setUintAttribute(collectionAddress, tokenId, 'X', 100n); + await tokenAttributes.setBytesAttribute(collectionAddress, tokenId, 'X', '0x1234'); + + expect(await tokenAttributes.getStringAttribute(collectionAddress, tokenId, 'X')).to.eql( + 'test description', ); - await tokenAttributes.setBytesAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', + expect(await tokenAttributes.getBoolAttribute(collectionAddress, tokenId, 'X')).to.eql(true); + expect(await tokenAttributes.getAddressAttribute(collectionAddress, tokenId, 'X')).to.eql( + tokenOwner.address, + ); + expect(await tokenAttributes.getUintAttribute(collectionAddress, tokenId, 'X')).to.eql(100n); + expect(await tokenAttributes.getBytesAttribute(collectionAddress, tokenId, 'X')).to.eql( '0x1234', ); - - expect( - await tokenAttributes.getStringAttribute(await ownedCollection.getAddress(), tokenId, 'X'), - ).to.eql('test description'); - expect( - await tokenAttributes.getBoolAttribute(await ownedCollection.getAddress(), tokenId, 'X'), - ).to.eql(true); - expect( - await tokenAttributes.getAddressAttribute(await ownedCollection.getAddress(), tokenId, 'X'), - ).to.eql(tokenOwner.address); - expect( - await tokenAttributes.getUintAttribute(await ownedCollection.getAddress(), tokenId, 'X'), - ).to.eql(100n); - expect( - await tokenAttributes.getBytesAttribute(await ownedCollection.getAddress(), tokenId, 'X'), - ).to.eql('0x1234'); }); it('can reuse string values and values are fine', async function () { - await tokenAttributes.setStringAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', + await tokenAttributes.setStringAttribute(collectionAddress, tokenId, 'X', 'common string'); + await tokenAttributes.setStringAttribute(collectionAddress, tokenId2, 'X', 'common string'); + + expect(await tokenAttributes.getStringAttribute(collectionAddress, tokenId, 'X')).to.eql( 'common string', ); - await tokenAttributes.setStringAttribute( - await ownedCollection.getAddress(), - tokenId2, - 'X', + expect(await tokenAttributes.getStringAttribute(collectionAddress, tokenId2, 'X')).to.eql( 'common string', ); - - expect( - await tokenAttributes.getStringAttribute(await ownedCollection.getAddress(), tokenId, 'X'), - ).to.eql('common string'); - expect( - await tokenAttributes.getStringAttribute(await ownedCollection.getAddress(), tokenId2, 'X'), - ).to.eql('common string'); }); it('should not allow to set string values to unauthorized caller', async function () { await expect( tokenAttributes .connect(tokenOwner) - .setStringAttribute(await ownedCollection.getAddress(), tokenId, 'X', 'test description'), + .setStringAttribute(collectionAddress, tokenId, 'X', 'test description'), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuer'); }); it('should not allow to set uint values to unauthorized caller', async function () { await expect( - tokenAttributes - .connect(tokenOwner) - .setUintAttribute(await ownedCollection.getAddress(), tokenId, 'X', 42n), + tokenAttributes.connect(tokenOwner).setUintAttribute(collectionAddress, tokenId, 'X', 42n), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuer'); }); it('should not allow to set boolean values to unauthorized caller', async function () { await expect( - tokenAttributes - .connect(tokenOwner) - .setBoolAttribute(await ownedCollection.getAddress(), tokenId, 'X', true), + tokenAttributes.connect(tokenOwner).setBoolAttribute(collectionAddress, tokenId, 'X', true), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuer'); }); @@ -840,12 +723,7 @@ describe('RMRKTokenAttributesRepository', async function () { await expect( tokenAttributes .connect(tokenOwner) - .setAddressAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - tokenOwner.address, - ), + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuer'); }); @@ -853,37 +731,33 @@ describe('RMRKTokenAttributesRepository', async function () { await expect( tokenAttributes .connect(tokenOwner) - .setBytesAttribute(await ownedCollection.getAddress(), tokenId, 'X', '0x1234'), + .setBytesAttribute(collectionAddress, tokenId, 'X', '0x1234'), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuer'); }); }); describe('Token attributes access control', async function () { - it('should not allow registering an already registered collection', async function () { + it('should allow registering an already registered collection', async function () { await tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), false, ); await expect( tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), false, ), - ).to.be.revertedWithCustomError(tokenAttributes, 'CollectionAlreadyRegistered'); + ).to.emit(tokenAttributes, 'AccessControlRegistration'); }); it('should not allow to register a collection if caller is not the owner of the collection', async function () { await expect( tokenAttributes .connect(tokenOwner) - .registerAccessControl( - await ownedCollection.getAddress(), - await collectionOwner.getAddress(), - true, - ), + .registerAccessControl(collectionAddress, await collectionOwner.getAddress(), true), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuer'); }); @@ -901,7 +775,7 @@ describe('RMRKTokenAttributesRepository', async function () { it('should allow to manage access control for registered collections', async function () { await tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), false, ); @@ -910,19 +784,19 @@ describe('RMRKTokenAttributesRepository', async function () { await tokenAttributes .connect(collectionOwner) .manageAccessControl( - await ownedCollection.getAddress(), + collectionAddress, 'X', AccessType.IssuerOrCollaborator, tokenOwner.address, ), ) .to.emit(tokenAttributes, 'AccessControlUpdate') - .withArgs(await ownedCollection.getAddress(), 'X', 2, tokenOwner); + .withArgs(collectionAddress, 'X', 2, tokenOwner); }); it('should allow issuer to manage collaborators', async function () { await tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), false, ); @@ -930,23 +804,23 @@ describe('RMRKTokenAttributesRepository', async function () { expect( await tokenAttributes .connect(collectionOwner) - .manageCollaborators(await ownedCollection.getAddress(), [tokenOwner.address], [true]), + .manageCollaborators(collectionAddress, [tokenOwner.address], [true]), ) .to.emit(tokenAttributes, 'CollaboratorUpdate') - .withArgs(await ownedCollection.getAddress(), [tokenOwner.address], [true]); + .withArgs(collectionAddress, [tokenOwner.address], [true]); }); it('should not allow to manage collaborators of an unregistered collection', async function () { await expect( tokenAttributes .connect(collectionOwner) - .manageCollaborators(await ownedCollection.getAddress(), [tokenOwner.address], [true]), + .manageCollaborators(collectionAddress, [tokenOwner.address], [true]), ).to.be.revertedWithCustomError(tokenAttributes, 'CollectionNotRegistered'); }); it('should not allow to manage collaborators if the caller is not the issuer', async function () { await tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), false, ); @@ -954,13 +828,13 @@ describe('RMRKTokenAttributesRepository', async function () { await expect( tokenAttributes .connect(tokenOwner) - .manageCollaborators(await ownedCollection.getAddress(), [tokenOwner.address], [true]), + .manageCollaborators(collectionAddress, [tokenOwner.address], [true]), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuer'); }); it('should not allow to manage collaborators for registered collections if collaborator arrays are not of equal length', async function () { await tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), false, ); @@ -969,7 +843,7 @@ describe('RMRKTokenAttributesRepository', async function () { tokenAttributes .connect(collectionOwner) .manageCollaborators( - await ownedCollection.getAddress(), + collectionAddress, [tokenOwner.address, await collectionOwner.getAddress()], [true], ), @@ -981,7 +855,7 @@ describe('RMRKTokenAttributesRepository', async function () { tokenAttributes .connect(collectionOwner) .manageAccessControl( - await ownedCollection.getAddress(), + collectionAddress, 'X', AccessType.IssuerOrCollaborator, tokenOwner.address, @@ -991,7 +865,7 @@ describe('RMRKTokenAttributesRepository', async function () { it('should not allow to manage access control if the caller is not issuer', async function () { await tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), false, ); @@ -1000,7 +874,7 @@ describe('RMRKTokenAttributesRepository', async function () { tokenAttributes .connect(tokenOwner) .manageAccessControl( - await ownedCollection.getAddress(), + collectionAddress, 'X', AccessType.IssuerOrCollaborator, tokenOwner.address, @@ -1010,7 +884,7 @@ describe('RMRKTokenAttributesRepository', async function () { it('should not allow to manage access control if the caller is not returned as collection owner when using ownable', async function () { await tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), true, ); @@ -1019,7 +893,7 @@ describe('RMRKTokenAttributesRepository', async function () { tokenAttributes .connect(tokenOwner) .manageAccessControl( - await ownedCollection.getAddress(), + collectionAddress, 'X', AccessType.IssuerOrCollaborator, tokenOwner.address, @@ -1029,155 +903,100 @@ describe('RMRKTokenAttributesRepository', async function () { it('should return the expected value when checking for collaborators', async function () { await tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), false, ); - expect( - await tokenAttributes.isCollaborator( - tokenOwner.address, - await ownedCollection.getAddress(), - ), - ).to.be.false; + expect(await tokenAttributes.isCollaborator(tokenOwner.address, collectionAddress)).to.be + .false; await tokenAttributes .connect(collectionOwner) - .manageCollaborators(await ownedCollection.getAddress(), [tokenOwner.address], [true]); + .manageCollaborators(collectionAddress, [tokenOwner.address], [true]); - expect( - await tokenAttributes.isCollaborator( - tokenOwner.address, - await ownedCollection.getAddress(), - ), - ).to.be.true; + expect(await tokenAttributes.isCollaborator(tokenOwner.address, collectionAddress)).to.be + .true; }); it('should return the expected value when checking for specific addresses', async function () { await tokenAttributes.registerAccessControl( - await ownedCollection.getAddress(), + collectionAddress, await collectionOwner.getAddress(), false, ); - expect( - await tokenAttributes.isSpecificAddress( - tokenOwner.address, - await ownedCollection.getAddress(), - 'X', - ), - ).to.be.false; + expect(await tokenAttributes.isSpecificAddress(tokenOwner.address, collectionAddress, 'X')).to + .be.false; await tokenAttributes .connect(collectionOwner) .manageAccessControl( - await ownedCollection.getAddress(), + collectionAddress, 'X', AccessType.IssuerOrCollaborator, tokenOwner.address, ); - expect( - await tokenAttributes.isSpecificAddress( - tokenOwner.address, - await ownedCollection.getAddress(), - 'X', - ), - ).to.be.true; + expect(await tokenAttributes.isSpecificAddress(tokenOwner.address, collectionAddress, 'X')).to + .be.true; }); it('should use the issuer returned from the collection when using only issuer when only issuer is allowed to manage parameter', async function () { await tokenAttributes .connect(collectionOwner) - .registerAccessControl( - await ownedCollection.getAddress(), - await collectionOwner.getAddress(), - true, - ); + .registerAccessControl(collectionAddress, await collectionOwner.getAddress(), true); await tokenAttributes .connect(collectionOwner) - .manageAccessControl( - await ownedCollection.getAddress(), - 'X', - AccessType.Issuer, - ethers.ZeroAddress, - ); + .manageAccessControl(collectionAddress, 'X', AccessType.Issuer, ethers.ZeroAddress); await expect( tokenAttributes .connect(tokenOwner) - .setAddressAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - tokenOwner.address, - ), + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuer'); await expect( tokenAttributes .connect(tokenOwner) - .setAddressAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - tokenOwner.address, - ), + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuer'); }); it('should only allow collaborator to modify the parameters if only collaborator is allowed to modify them', async function () { await tokenAttributes .connect(collectionOwner) - .registerAccessControl( - await ownedCollection.getAddress(), - await collectionOwner.getAddress(), - false, - ); + .registerAccessControl(collectionAddress, await collectionOwner.getAddress(), false); await tokenAttributes .connect(collectionOwner) - .manageAccessControl( - await ownedCollection.getAddress(), - 'X', - AccessType.Collaborator, - ethers.ZeroAddress, - ); + .manageAccessControl(collectionAddress, 'X', AccessType.Collaborator, ethers.ZeroAddress); await tokenAttributes .connect(collectionOwner) - .manageCollaborators(await ownedCollection.getAddress(), [tokenOwner.address], [true]); + .manageCollaborators(collectionAddress, [tokenOwner.address], [true]); await tokenAttributes .connect(tokenOwner) - .setAddressAttribute(await ownedCollection.getAddress(), tokenId, 'X', tokenOwner.address); + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address); await expect( tokenAttributes .connect(collectionOwner) - .setAddressAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - tokenOwner.address, - ), + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionCollaborator'); }); it('should only allow issuer and collaborator to modify the parameters if only issuer and collaborator is allowed to modify them', async function () { await tokenAttributes .connect(collectionOwner) - .registerAccessControl( - await ownedCollection.getAddress(), - await collectionOwner.getAddress(), - false, - ); + .registerAccessControl(collectionAddress, await collectionOwner.getAddress(), false); await tokenAttributes .connect(collectionOwner) .manageAccessControl( - await ownedCollection.getAddress(), + collectionAddress, 'X', AccessType.IssuerOrCollaborator, ethers.ZeroAddress, @@ -1185,41 +1004,32 @@ describe('RMRKTokenAttributesRepository', async function () { await tokenAttributes .connect(collectionOwner) - .setAddressAttribute(await ownedCollection.getAddress(), tokenId, 'X', tokenOwner.address); + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address); await expect( tokenAttributes .connect(tokenOwner) - .setAddressAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - tokenOwner.address, - ), + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuerOrCollaborator'); await tokenAttributes .connect(collectionOwner) - .manageCollaborators(await ownedCollection.getAddress(), [tokenOwner.address], [true]); + .manageCollaborators(collectionAddress, [tokenOwner.address], [true]); await tokenAttributes .connect(tokenOwner) - .setAddressAttribute(await ownedCollection.getAddress(), tokenId, 'X', tokenOwner.address); + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address); }); it('should only allow issuer and collaborator to modify the parameters if only issuer and collaborator is allowed to modify them even when using the ownable', async function () { await tokenAttributes .connect(collectionOwner) - .registerAccessControl( - await ownedCollection.getAddress(), - await collectionOwner.getAddress(), - true, - ); + .registerAccessControl(collectionAddress, await collectionOwner.getAddress(), true); await tokenAttributes .connect(collectionOwner) .manageAccessControl( - await ownedCollection.getAddress(), + collectionAddress, 'X', AccessType.IssuerOrCollaborator, ethers.ZeroAddress, @@ -1227,90 +1037,58 @@ describe('RMRKTokenAttributesRepository', async function () { await tokenAttributes .connect(collectionOwner) - .setAddressAttribute(await ownedCollection.getAddress(), tokenId, 'X', tokenOwner.address); + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address); await expect( tokenAttributes .connect(tokenOwner) - .setAddressAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - tokenOwner.address, - ), + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address), ).to.be.revertedWithCustomError(tokenAttributes, 'NotCollectionIssuerOrCollaborator'); await tokenAttributes .connect(collectionOwner) - .manageCollaborators( - await ownedCollection.getAddress(), - [await collaborator.getAddress()], - [true], - ); + .manageCollaborators(collectionAddress, [await collaborator.getAddress()], [true]); await tokenAttributes .connect(collaborator) - .setAddressAttribute(await ownedCollection.getAddress(), tokenId, 'X', tokenOwner.address); + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address); }); it('should only allow token owner to modify the parameters if only token owner is allowed to modify them', async function () { await tokenAttributes .connect(collectionOwner) - .registerAccessControl( - await ownedCollection.getAddress(), - await collectionOwner.getAddress(), - false, - ); + .registerAccessControl(collectionAddress, await collectionOwner.getAddress(), false); await tokenAttributes .connect(collectionOwner) - .manageAccessControl( - await ownedCollection.getAddress(), - 'X', - AccessType.TokenOwner, - ethers.ZeroAddress, - ); + .manageAccessControl(collectionAddress, 'X', AccessType.TokenOwner, ethers.ZeroAddress); await expect( tokenAttributes .connect(collaborator) - .setAddressAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - tokenOwner.address, - ), + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address), ).to.be.revertedWithCustomError(tokenAttributes, 'NotTokenOwner'); await expect( tokenAttributes .connect(collectionOwner) - .setAddressAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - tokenOwner.address, - ), + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address), ).to.be.revertedWithCustomError(tokenAttributes, 'NotTokenOwner'); await tokenAttributes .connect(tokenOwner) - .setAddressAttribute(await ownedCollection.getAddress(), tokenId, 'X', tokenOwner.address); + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address); }); it('should only allow specific address to modify the parameters if only specific address is allowed to modify them', async function () { await tokenAttributes .connect(collectionOwner) - .registerAccessControl( - await ownedCollection.getAddress(), - await collectionOwner.getAddress(), - false, - ); + .registerAccessControl(collectionAddress, await collectionOwner.getAddress(), false); await tokenAttributes .connect(collectionOwner) .manageAccessControl( - await ownedCollection.getAddress(), + collectionAddress, 'X', AccessType.SpecificAddress, ethers.ZeroAddress, @@ -1319,18 +1097,13 @@ describe('RMRKTokenAttributesRepository', async function () { await expect( tokenAttributes .connect(tokenOwner) - .setAddressAttribute( - await ownedCollection.getAddress(), - tokenId, - 'X', - tokenOwner.address, - ), + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address), ).to.be.revertedWithCustomError(tokenAttributes, 'NotSpecificAddress'); await tokenAttributes .connect(collectionOwner) .manageAccessControl( - await ownedCollection.getAddress(), + collectionAddress, 'X', AccessType.SpecificAddress, tokenOwner.address, @@ -1338,48 +1111,44 @@ describe('RMRKTokenAttributesRepository', async function () { await tokenAttributes .connect(tokenOwner) - .setAddressAttribute(await ownedCollection.getAddress(), tokenId, 'X', tokenOwner.address); + .setAddressAttribute(collectionAddress, tokenId, 'X', tokenOwner.address); }); it('should allow to use presigned message to modify the parameters', async function () { await tokenAttributes .connect(collectionOwner) - .registerAccessControl( - await ownedCollection.getAddress(), - await collectionOwner.getAddress(), - false, - ); + .registerAccessControl(collectionAddress, await collectionOwner.getAddress(), false); const uintMessage = await tokenAttributes.prepareMessageToPresignUintAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 1, 9999999999n, ); const stringMessage = await tokenAttributes.prepareMessageToPresignStringAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 'test', 9999999999n, ); const boolMessage = await tokenAttributes.prepareMessageToPresignBoolAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', true, 9999999999n, ); const bytesMessage = await tokenAttributes.prepareMessageToPresignBytesAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', '0x1234', 9999999999n, ); const addressMessage = await tokenAttributes.prepareMessageToPresignAddressAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', tokenOwner.address, @@ -1417,7 +1186,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetUintAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 1, @@ -1428,13 +1197,13 @@ describe('RMRKTokenAttributesRepository', async function () { ), ) .to.emit(tokenAttributes, 'UintAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), 1, 'X', 1); + .withArgs(collectionAddress, 1, 'X', 1); await expect( tokenAttributes .connect(tokenOwner) .presignedSetStringAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 'test', @@ -1445,13 +1214,13 @@ describe('RMRKTokenAttributesRepository', async function () { ), ) .to.emit(tokenAttributes, 'StringAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), 1, 'X', 'test'); + .withArgs(collectionAddress, 1, 'X', 'test'); await expect( tokenAttributes .connect(tokenOwner) .presignedSetBoolAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', true, @@ -1462,13 +1231,13 @@ describe('RMRKTokenAttributesRepository', async function () { ), ) .to.emit(tokenAttributes, 'BoolAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), 1, 'X', true); + .withArgs(collectionAddress, 1, 'X', true); await expect( tokenAttributes .connect(tokenOwner) .presignedSetBytesAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', '0x1234', @@ -1479,13 +1248,13 @@ describe('RMRKTokenAttributesRepository', async function () { ), ) .to.emit(tokenAttributes, 'BytesAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), 1, 'X', '0x1234'); + .withArgs(collectionAddress, 1, 'X', '0x1234'); await expect( tokenAttributes .connect(tokenOwner) .presignedSetAddressAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', tokenOwner.address, @@ -1496,50 +1265,46 @@ describe('RMRKTokenAttributesRepository', async function () { ), ) .to.emit(tokenAttributes, 'AddressAttributeUpdated') - .withArgs(await ownedCollection.getAddress(), 1, 'X', tokenOwner.address); + .withArgs(collectionAddress, 1, 'X', tokenOwner.address); }); it('should not allow to use presigned message to modify the parameters if the deadline has elapsed', async function () { await tokenAttributes .connect(collectionOwner) - .registerAccessControl( - await ownedCollection.getAddress(), - await collectionOwner.getAddress(), - false, - ); + .registerAccessControl(collectionAddress, await collectionOwner.getAddress(), false); await mine(1000, { interval: 15 }); const uintMessage = await tokenAttributes.prepareMessageToPresignUintAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 1, 10n, ); const stringMessage = await tokenAttributes.prepareMessageToPresignStringAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 'test', 10n, ); const boolMessage = await tokenAttributes.prepareMessageToPresignBoolAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', true, 10n, ); const bytesMessage = await tokenAttributes.prepareMessageToPresignBytesAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', '0x1234', 10n, ); const addressMessage = await tokenAttributes.prepareMessageToPresignAddressAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', tokenOwner.address, @@ -1577,7 +1342,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetUintAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 1, @@ -1592,7 +1357,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetStringAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 'test', @@ -1607,7 +1372,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetBoolAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', true, @@ -1622,7 +1387,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetBytesAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', '0x1234', @@ -1637,7 +1402,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetAddressAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', tokenOwner.address, @@ -1652,42 +1417,38 @@ describe('RMRKTokenAttributesRepository', async function () { it('should not allow to use presigned message to modify the parameters if the setter does not match the actual signer', async function () { await tokenAttributes .connect(collectionOwner) - .registerAccessControl( - await ownedCollection.getAddress(), - await collectionOwner.getAddress(), - false, - ); + .registerAccessControl(collectionAddress, await collectionOwner.getAddress(), false); const uintMessage = await tokenAttributes.prepareMessageToPresignUintAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 1, 9999999999n, ); const stringMessage = await tokenAttributes.prepareMessageToPresignStringAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 'test', 9999999999n, ); const boolMessage = await tokenAttributes.prepareMessageToPresignBoolAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', true, 9999999999n, ); const bytesMessage = await tokenAttributes.prepareMessageToPresignBytesAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', '0x1234', 9999999999n, ); const addressMessage = await tokenAttributes.prepareMessageToPresignAddressAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', tokenOwner.address, @@ -1725,7 +1486,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetUintAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 1, @@ -1740,7 +1501,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetStringAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 'test', @@ -1755,7 +1516,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetBoolAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', true, @@ -1770,7 +1531,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetBytesAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', '0x1234', @@ -1785,7 +1546,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetAddressAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', tokenOwner.address, @@ -1799,35 +1560,35 @@ describe('RMRKTokenAttributesRepository', async function () { it('should not allow to use presigned message to modify the parameters if the signer is not authorized to modify them', async function () { const uintMessage = await tokenAttributes.prepareMessageToPresignUintAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 1, 9999999999n, ); const stringMessage = await tokenAttributes.prepareMessageToPresignStringAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 'test', 9999999999n, ); const boolMessage = await tokenAttributes.prepareMessageToPresignBoolAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', true, 9999999999n, ); const bytesMessage = await tokenAttributes.prepareMessageToPresignBytesAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', '0x1234', 9999999999n, ); const addressMessage = await tokenAttributes.prepareMessageToPresignAddressAttribute( - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', tokenOwner.address, @@ -1865,7 +1626,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetUintAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 1, @@ -1880,7 +1641,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetStringAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', 'test', @@ -1895,7 +1656,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetBoolAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', true, @@ -1910,7 +1671,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetBytesAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', '0x1234', @@ -1925,7 +1686,7 @@ describe('RMRKTokenAttributesRepository', async function () { .connect(tokenOwner) .presignedSetAddressAttribute( await collectionOwner.getAddress(), - await ownedCollection.getAddress(), + collectionAddress, tokenId, 'X', tokenOwner.address,