--- a/.maintain/scripts/generate_api.sh +++ b/.maintain/scripts/generate_api.sh @@ -7,6 +7,5 @@ sed -n '/=== SNIP START ===/, /=== SNIP END ===/{ /=== SNIP START ===/! { /=== SNIP END ===/! p } }' $tmp > $raw formatted=$(mktemp) prettier --use-tabs $raw > $formatted -solhint --fix $formatted mv $formatted $OUTPUT --- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -21,7 +21,7 @@ use sp_std::vec::Vec; use up_data_structs::Property; -use crate::{Pallet, CollectionHandle, Config}; +use crate::{Pallet, CollectionHandle, Config, CollectionProperties}; /// Does not always represent a full collection, for RFT it is either /// collection (Implementing ERC721), or specific collection token (Implementing ERC20) @@ -33,24 +33,35 @@ #[solidity_interface(name = "CollectionProperties")] impl CollectionHandle { - fn set_property(&mut self, caller: caller, key: string, value: string) -> Result<()> { - >::set_collection_property( - self, - &T::CrossAccountId::from_eth(caller), - Property { - key: >::from(key) - .try_into() - .map_err(|_| "key too large")?, - value: >::from(value) - .try_into() - .map_err(|_| "value too large")?, - }, - ) - .map_err(dispatch_to_evm::)?; - Ok(()) + fn set_collection_property(&mut self, caller: caller, key: string, value: bytes) -> Result<()> { + let caller = T::CrossAccountId::from_eth(caller); + let key = >::from(key) + .try_into() + .map_err(|_| "key too large")?; + let value = value.try_into().map_err(|_| "value too large")?; + + >::set_collection_property(self, &caller, Property { key, value }) + .map_err(dispatch_to_evm::) } - fn delete_property(&mut self, caller: caller, key: string) -> Result<()> { - self.set_property(caller, key, string::new()) + fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> { + let caller = T::CrossAccountId::from_eth(caller); + let key = >::from(key) + .try_into() + .map_err(|_| "key too large")?; + + >::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::) + } + + /// Throws error if key not found + fn collection_property(&self, key: string) -> Result { + let key = >::from(key) + .try_into() + .map_err(|_| "key too large")?; + + let props = >::get(self.id); + let prop = props.get(&key).ok_or("key not found")?; + + Ok(prop.to_vec()) } } --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -31,12 +31,12 @@ use pallet_evm::GasWeightMapping; use up_data_structs::{ COLLECTION_NUMBER_LIMIT, Collection, RpcCollection, CollectionId, CreateItemData, - MAX_TOKEN_PREFIX_LENGTH, COLLECTION_ADMINS_LIMIT, TokenId, - CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode, NFT_SPONSOR_TRANSFER_TIMEOUT, + MAX_TOKEN_PREFIX_LENGTH, COLLECTION_ADMINS_LIMIT, TokenId, CollectionStats, + MAX_TOKEN_OWNERSHIP, CollectionMode, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, - CUSTOM_DATA_LIMIT, CollectionLimits, CreateCollectionData, SponsorshipState, - CreateItemExData, SponsoringRateLimit, budget::Budget, COLLECTION_FIELD_LIMIT, CollectionField, - PhantomType, Property, Properties, PropertiesPermissionMap, PropertyKey, PropertyPermission, + CUSTOM_DATA_LIMIT, CollectionLimits, CreateCollectionData, SponsorshipState, CreateItemExData, + SponsoringRateLimit, budget::Budget, COLLECTION_FIELD_LIMIT, CollectionField, PhantomType, + Property, Properties, PropertiesPermissionMap, PropertyKey, PropertyPermission, PropertiesError, PropertyKeyPermission, TokenData, TrySet, }; pub use pallet::*; --- a/pallets/fungible/src/stubs/UniqueFungible.sol +++ b/pallets/fungible/src/stubs/UniqueFungible.sol @@ -8,8 +8,13 @@ uint8 dummy; string stub_error = "this contract is implemented in native"; } + contract ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) external view returns (bool) { + function supportsInterface(bytes4 interfaceID) + external + view + returns (bool) + { require(false, stub_error); interfaceID; return true; @@ -19,24 +24,11 @@ // Inline contract ERC20Events { event Transfer(address indexed from, address indexed to, uint256 value); - event Approval(address indexed owner, address indexed spender, uint256 value); -} - -// Selector: 56fd500b -contract CollectionProperties is Dummy, ERC165 { - // Selector: setProperty(string,string) 62d9491f - function setProperty(string memory key, string memory value) public { - require(false, stub_error); - key; - value; - dummy = 0; - } - // Selector: deleteProperty(string) 34241914 - function deleteProperty(string memory key) public { - require(false, stub_error); - key; - dummy = 0; - } + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); } // Selector: 79cc6790 @@ -59,24 +51,28 @@ dummy; return ""; } + // Selector: symbol() 95d89b41 function symbol() public view returns (string memory) { require(false, stub_error); dummy; return ""; } + // Selector: totalSupply() 18160ddd function totalSupply() public view returns (uint256) { require(false, stub_error); dummy; return 0; } + // Selector: decimals() 313ce567 function decimals() public view returns (uint8) { require(false, stub_error); dummy; return 0; } + // Selector: balanceOf(address) 70a08231 function balanceOf(address owner) public view returns (uint256) { require(false, stub_error); @@ -84,6 +80,7 @@ dummy; return 0; } + // Selector: transfer(address,uint256) a9059cbb function transfer(address to, uint256 amount) public returns (bool) { require(false, stub_error); @@ -92,8 +89,13 @@ dummy = 0; return false; } + // Selector: transferFrom(address,address,uint256) 23b872dd - function transferFrom(address from, address to, uint256 amount) public returns (bool) { + function transferFrom( + address from, + address to, + uint256 amount + ) public returns (bool) { require(false, stub_error); from; to; @@ -101,6 +103,7 @@ dummy = 0; return false; } + // Selector: approve(address,uint256) 095ea7b3 function approve(address spender, uint256 amount) public returns (bool) { require(false, stub_error); @@ -109,8 +112,13 @@ dummy = 0; return false; } + // Selector: allowance(address,address) dd62ed3e - function allowance(address owner, address spender) public view returns (uint256) { + function allowance(address owner, address spender) + public + view + returns (uint256) + { require(false, stub_error); owner; spender; @@ -119,6 +127,44 @@ } } -contract UniqueFungible is Dummy, ERC165, ERC20, ERC20UniqueExtensions, CollectionProperties { +// Selector: 9b5e29c5 +contract CollectionProperties is Dummy, ERC165 { + // Selector: setCollectionProperty(string,bytes) 2f073f66 + function setCollectionProperty(string memory key, bytes memory value) + public + { + require(false, stub_error); + key; + value; + dummy = 0; + } + + // Selector: deleteCollectionProperty(string) 7b7debce + function deleteCollectionProperty(string memory key) public { + require(false, stub_error); + key; + dummy = 0; + } + + // Throws error if key not found + // + // Selector: collectionProperty(string) cf24fd6d + function collectionProperty(string memory key) + public + view + returns (bytes memory) + { + require(false, stub_error); + key; + dummy; + return hex""; + } } +contract UniqueFungible is + Dummy, + ERC165, + ERC20, + ERC20UniqueExtensions, + CollectionProperties +{} --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -21,7 +21,7 @@ }; use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight}; use frame_support::BoundedVec; -use up_data_structs::{TokenId, SchemaVersion}; +use up_data_structs::{TokenId, SchemaVersion, PropertyPermission, PropertyKeyPermission, Property}; use pallet_evm_coder_substrate::dispatch_to_evm; use sp_core::{H160, U256}; use sp_std::vec::Vec; @@ -35,9 +35,80 @@ use crate::{ AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted, - SelfWeightOf, weights::WeightInfo, + SelfWeightOf, weights::WeightInfo, TokenProperties, }; +#[solidity_interface(name = "TokenProperties")] +impl NonfungibleHandle { + fn set_token_property_permission( + &mut self, + caller: caller, + key: string, + is_mutable: bool, + collection_admin: bool, + token_owner: bool, + ) -> Result<()> { + let caller = T::CrossAccountId::from_eth(caller); + >::set_property_permission( + self, + &caller, + PropertyKeyPermission { + key: >::from(key) + .try_into() + .map_err(|_| "too long key")?, + permission: PropertyPermission { + mutable: is_mutable, + collection_admin, + token_owner, + }, + }, + ) + .map_err(dispatch_to_evm::) + } + + fn set_property( + &mut self, + caller: caller, + token_id: uint256, + key: string, + value: bytes, + ) -> Result<()> { + let caller = T::CrossAccountId::from_eth(caller); + let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?; + let key = >::from(key) + .try_into() + .map_err(|_| "key too long")?; + let value = value.try_into().map_err(|_| "value too long")?; + + >::set_token_property(self, &caller, TokenId(token_id), Property { key, value }) + .map_err(dispatch_to_evm::) + } + + fn delete_property(&mut self, token_id: uint256, caller: caller, key: string) -> Result<()> { + let caller = T::CrossAccountId::from_eth(caller); + let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?; + let key = >::from(key) + .try_into() + .map_err(|_| "key too long")?; + + >::delete_token_property(self, &caller, TokenId(token_id), key) + .map_err(dispatch_to_evm::) + } + + /// Throws error if key not found + fn property(&self, token_id: uint256, key: string) -> Result { + let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?; + let key = >::from(key) + .try_into() + .map_err(|_| "key too long")?; + + let props = >::get((self.id, token_id)); + let prop = props.get(&key).ok_or("key not found")?; + + Ok(prop.to_vec()) + } +} + fn error_unsupported_schema_version() -> Error { alloc::format!( "Unsupported schema version! Support only {:?}", @@ -470,7 +541,8 @@ ERC721UniqueExtensions, ERC721Mintable, ERC721Burnable, - via("CollectionHandle", common_mut, CollectionProperties) + via("CollectionHandle", common_mut, CollectionProperties), + TokenProperties, ) )] impl NonfungibleHandle {} --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -63,7 +63,9 @@ #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, traits::StorageVersion}; + use frame_support::{ + Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, traits::StorageVersion, + }; use frame_system::pallet_prelude::*; use up_data_structs::{CollectionId, TokenId}; use super::weights::WeightInfo; @@ -429,6 +431,14 @@ >::set_property_permissions(collection, sender, property_permissions) } + pub fn set_property_permission( + collection: &CollectionHandle, + sender: &T::CrossAccountId, + permission: PropertyKeyPermission, + ) -> DispatchResult { + >::set_property_permission(collection, sender, permission) + } + pub fn transfer( collection: &NonfungibleHandle, from: &T::CrossAccountId, --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -51,32 +51,68 @@ event MintingFinished(); } -// Selector: 42966c68 -contract ERC721Burnable is Dummy, ERC165 { - // Selector: burn(uint256) 42966c68 - function burn(uint256 tokenId) public { +// Selector: 41369377 +contract TokenProperties is Dummy, ERC165 { + // Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa + function setTokenPropertyPermission( + string memory key, + bool isMutable, + bool collectionAdmin, + bool tokenOwner + ) public { require(false, stub_error); - tokenId; + key; + isMutable; + collectionAdmin; + tokenOwner; dummy = 0; } -} -// Selector: 56fd500b -contract CollectionProperties is Dummy, ERC165 { - // Selector: setProperty(string,string) 62d9491f - function setProperty(string memory key, string memory value) public { + // Selector: setProperty(uint256,string,bytes) 1752d67b + function setProperty( + uint256 tokenId, + string memory key, + bytes memory value + ) public { require(false, stub_error); + tokenId; key; value; dummy = 0; } - // Selector: deleteProperty(string) 34241914 - function deleteProperty(string memory key) public { + // Selector: deleteProperty(uint256,string) 066111d1 + function deleteProperty(uint256 tokenId, string memory key) public { require(false, stub_error); + tokenId; key; dummy = 0; } + + // Throws error if key not found + // + // Selector: property(uint256,string) 7228c327 + function property(uint256 tokenId, string memory key) + public + view + returns (bytes memory) + { + require(false, stub_error); + tokenId; + key; + dummy; + return hex""; + } +} + +// Selector: 42966c68 +contract ERC721Burnable is Dummy, ERC165 { + // Selector: burn(uint256) 42966c68 + function burn(uint256 tokenId) public { + require(false, stub_error); + tokenId; + dummy = 0; + } } // Selector: 58800161 @@ -294,6 +330,40 @@ } } +// Selector: 9b5e29c5 +contract CollectionProperties is Dummy, ERC165 { + // Selector: setCollectionProperty(string,bytes) 2f073f66 + function setCollectionProperty(string memory key, bytes memory value) + public + { + require(false, stub_error); + key; + value; + dummy = 0; + } + + // Selector: deleteCollectionProperty(string) 7b7debce + function deleteCollectionProperty(string memory key) public { + require(false, stub_error); + key; + dummy = 0; + } + + // Throws error if key not found + // + // Selector: collectionProperty(string) cf24fd6d + function collectionProperty(string memory key) + public + view + returns (bytes memory) + { + require(false, stub_error); + key; + dummy; + return hex""; + } +} + // Selector: d74d154f contract ERC721UniqueExtensions is Dummy, ERC165 { // Selector: transfer(address,uint256) a9059cbb @@ -353,5 +423,6 @@ ERC721UniqueExtensions, ERC721Mintable, ERC721Burnable, - CollectionProperties + CollectionProperties, + TokenProperties {} --- a/tests/src/eth/api/UniqueFungible.sol +++ b/tests/src/eth/api/UniqueFungible.sol @@ -22,15 +22,6 @@ ); } -// Selector: 56fd500b -interface CollectionProperties is Dummy, ERC165 { - // Selector: setProperty(string,string) 62d9491f - function setProperty(string memory key, string memory value) external; - - // Selector: deleteProperty(string) 34241914 - function deleteProperty(string memory key) external; -} - // Selector: 79cc6790 interface ERC20UniqueExtensions is Dummy, ERC165 { // Selector: burnFrom(address,uint256) 79cc6790 @@ -74,6 +65,24 @@ returns (uint256); } +// Selector: 9b5e29c5 +interface CollectionProperties is Dummy, ERC165 { + // Selector: setCollectionProperty(string,bytes) 2f073f66 + function setCollectionProperty(string memory key, bytes memory value) + external; + + // Selector: deleteCollectionProperty(string) 7b7debce + function deleteCollectionProperty(string memory key) external; + + // Throws error if key not found + // + // Selector: collectionProperty(string) cf24fd6d + function collectionProperty(string memory key) + external + view + returns (bytes memory); +} + interface UniqueFungible is Dummy, ERC165, --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -42,21 +42,41 @@ event MintingFinished(); } +// Selector: 41369377 +interface TokenProperties is Dummy, ERC165 { + // Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa + function setTokenPropertyPermission( + string memory key, + bool isMutable, + bool collectionAdmin, + bool tokenOwner + ) external; + + // Selector: setProperty(uint256,string,bytes) 1752d67b + function setProperty( + uint256 tokenId, + string memory key, + bytes memory value + ) external; + + // Selector: deleteProperty(uint256,string) 066111d1 + function deleteProperty(uint256 tokenId, string memory key) external; + + // Throws error if key not found + // + // Selector: property(uint256,string) 7228c327 + function property(uint256 tokenId, string memory key) + external + view + returns (bytes memory); +} + // Selector: 42966c68 interface ERC721Burnable is Dummy, ERC165 { // Selector: burn(uint256) 42966c68 function burn(uint256 tokenId) external; } -// Selector: 56fd500b -interface CollectionProperties is Dummy, ERC165 { - // Selector: setProperty(string,string) 62d9491f - function setProperty(string memory key, string memory value) external; - - // Selector: deleteProperty(string) 34241914 - function deleteProperty(string memory key) external; -} - // Selector: 58800161 interface ERC721 is Dummy, ERC165, ERC721Events { // Selector: balanceOf(address) 70a08231 @@ -171,6 +191,24 @@ function totalSupply() external view returns (uint256); } +// Selector: 9b5e29c5 +interface CollectionProperties is Dummy, ERC165 { + // Selector: setCollectionProperty(string,bytes) 2f073f66 + function setCollectionProperty(string memory key, bytes memory value) + external; + + // Selector: deleteCollectionProperty(string) 7b7debce + function deleteCollectionProperty(string memory key) external; + + // Throws error if key not found + // + // Selector: collectionProperty(string) cf24fd6d + function collectionProperty(string memory key) + external + view + returns (bytes memory); +} + // Selector: d74d154f interface ERC721UniqueExtensions is Dummy, ERC165 { // Selector: transfer(address,uint256) a9059cbb @@ -202,5 +240,6 @@ ERC721UniqueExtensions, ERC721Mintable, ERC721Burnable, - CollectionProperties + CollectionProperties, + TokenProperties {}