difftreelog
feat implement evm property manipulation
in: master
12 files changed
.maintain/scripts/generate_api.shdiffbeforeafterboth--- 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
pallets/common/src/erc.rsdiffbeforeafterboth--- 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<T: Config> CollectionHandle<T> {
- fn set_property(&mut self, caller: caller, key: string, value: string) -> Result<()> {
- <Pallet<T>>::set_collection_property(
- self,
- &T::CrossAccountId::from_eth(caller),
- Property {
- key: <Vec<u8>>::from(key)
- .try_into()
- .map_err(|_| "key too large")?,
- value: <Vec<u8>>::from(value)
- .try_into()
- .map_err(|_| "value too large")?,
- },
- )
- .map_err(dispatch_to_evm::<T>)?;
- Ok(())
+ fn set_collection_property(&mut self, caller: caller, key: string, value: bytes) -> Result<()> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let key = <Vec<u8>>::from(key)
+ .try_into()
+ .map_err(|_| "key too large")?;
+ let value = value.try_into().map_err(|_| "value too large")?;
+
+ <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })
+ .map_err(dispatch_to_evm::<T>)
}
- 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 = <Vec<u8>>::from(key)
+ .try_into()
+ .map_err(|_| "key too large")?;
+
+ <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)
+ }
+
+ /// Throws error if key not found
+ fn collection_property(&self, key: string) -> Result<bytes> {
+ let key = <Vec<u8>>::from(key)
+ .try_into()
+ .map_err(|_| "key too large")?;
+
+ let props = <CollectionProperties<T>>::get(self.id);
+ let prop = props.get(&key).ok_or("key not found")?;
+
+ Ok(prop.to_vec())
}
}
pallets/common/src/lib.rsdiffbeforeafterboth--- 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::*;
pallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth--- 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
+{}
pallets/nonfungible/src/erc.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617extern crate alloc;18use core::{19 char::{REPLACEMENT_CHARACTER, decode_utf16},20 convert::TryInto,21};22use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};23use frame_support::BoundedVec;24use up_data_structs::{TokenId, SchemaVersion};25use pallet_evm_coder_substrate::dispatch_to_evm;26use sp_core::{H160, U256};27use sp_std::vec::Vec;28use pallet_common::{29 erc::{CommonEvmHandler, PrecompileResult, CollectionPropertiesCall},30 CollectionHandle,31};32use pallet_evm::account::CrossAccountId;33use pallet_evm_coder_substrate::call;34use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};3536use crate::{37 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,38 SelfWeightOf, weights::WeightInfo,39};4041fn error_unsupported_schema_version() -> Error {42 alloc::format!(43 "Unsupported schema version! Support only {:?}",44 SchemaVersion::ImageURL45 )46 .as_str()47 .into()48}4950#[derive(ToLog)]51pub enum ERC721Events {52 Transfer {53 #[indexed]54 from: address,55 #[indexed]56 to: address,57 #[indexed]58 token_id: uint256,59 },60 Approval {61 #[indexed]62 owner: address,63 #[indexed]64 approved: address,65 #[indexed]66 token_id: uint256,67 },68 #[allow(dead_code)]69 ApprovalForAll {70 #[indexed]71 owner: address,72 #[indexed]73 operator: address,74 approved: bool,75 },76}7778#[derive(ToLog)]79pub enum ERC721MintableEvents {80 #[allow(dead_code)]81 MintingFinished {},82}8384#[solidity_interface(name = "ERC721Metadata")]85impl<T: Config> NonfungibleHandle<T> {86 fn name(&self) -> Result<string> {87 Ok(decode_utf16(self.name.iter().copied())88 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))89 .collect::<string>())90 }9192 fn symbol(&self) -> Result<string> {93 Ok(string::from_utf8_lossy(&self.token_prefix).into())94 }9596 /// Returns token's const_metadata97 #[solidity(rename_selector = "tokenURI")]98 fn token_uri(&self, token_id: uint256) -> Result<string> {99 if !matches!(self.schema_version, SchemaVersion::ImageURL) {100 return Err(error_unsupported_schema_version());101 }102103 self.consume_store_reads(1)?;104 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;105 Ok(string::from_utf8_lossy(106 &<TokenData<T>>::get((self.id, token_id))107 .ok_or("token not found")?108 .const_data,109 )110 .into())111 }112}113114#[solidity_interface(name = "ERC721Enumerable")]115impl<T: Config> NonfungibleHandle<T> {116 fn token_by_index(&self, index: uint256) -> Result<uint256> {117 Ok(index)118 }119120 /// Not implemented121 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {122 // TODO: Not implemetable123 Err("not implemented".into())124 }125126 fn total_supply(&self) -> Result<uint256> {127 self.consume_store_reads(1)?;128 Ok(<Pallet<T>>::total_supply(self).into())129 }130}131132#[solidity_interface(name = "ERC721", events(ERC721Events))]133impl<T: Config> NonfungibleHandle<T> {134 fn balance_of(&self, owner: address) -> Result<uint256> {135 self.consume_store_reads(1)?;136 let owner = T::CrossAccountId::from_eth(owner);137 let balance = <AccountBalance<T>>::get((self.id, owner));138 Ok(balance.into())139 }140 fn owner_of(&self, token_id: uint256) -> Result<address> {141 self.consume_store_reads(1)?;142 let token: TokenId = token_id.try_into()?;143 Ok(*<TokenData<T>>::get((self.id, token))144 .ok_or("token not found")?145 .owner146 .as_eth())147 }148 /// Not implemented149 fn safe_transfer_from_with_data(150 &mut self,151 _from: address,152 _to: address,153 _token_id: uint256,154 _data: bytes,155 _value: value,156 ) -> Result<void> {157 // TODO: Not implemetable158 Err("not implemented".into())159 }160 /// Not implemented161 fn safe_transfer_from(162 &mut self,163 _from: address,164 _to: address,165 _token_id: uint256,166 _value: value,167 ) -> Result<void> {168 // TODO: Not implemetable169 Err("not implemented".into())170 }171172 #[weight(<SelfWeightOf<T>>::transfer_from())]173 fn transfer_from(174 &mut self,175 caller: caller,176 from: address,177 to: address,178 token_id: uint256,179 _value: value,180 ) -> Result<void> {181 let caller = T::CrossAccountId::from_eth(caller);182 let from = T::CrossAccountId::from_eth(from);183 let to = T::CrossAccountId::from_eth(to);184 let token = token_id.try_into()?;185 let budget = self186 .recorder187 .weight_calls_budget(<StructureWeight<T>>::find_parent());188189 <Pallet<T>>::transfer_from(self, &caller, &from, &to, token, &budget)190 .map_err(dispatch_to_evm::<T>)?;191 Ok(())192 }193194 #[weight(<SelfWeightOf<T>>::approve())]195 fn approve(196 &mut self,197 caller: caller,198 approved: address,199 token_id: uint256,200 _value: value,201 ) -> Result<void> {202 let caller = T::CrossAccountId::from_eth(caller);203 let approved = T::CrossAccountId::from_eth(approved);204 let token = token_id.try_into()?;205206 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))207 .map_err(dispatch_to_evm::<T>)?;208 Ok(())209 }210211 /// Not implemented212 fn set_approval_for_all(213 &mut self,214 _caller: caller,215 _operator: address,216 _approved: bool,217 ) -> Result<void> {218 // TODO: Not implemetable219 Err("not implemented".into())220 }221222 /// Not implemented223 fn get_approved(&self, _token_id: uint256) -> Result<address> {224 // TODO: Not implemetable225 Err("not implemented".into())226 }227228 /// Not implemented229 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {230 // TODO: Not implemetable231 Err("not implemented".into())232 }233}234235#[solidity_interface(name = "ERC721Burnable")]236impl<T: Config> NonfungibleHandle<T> {237 #[weight(<SelfWeightOf<T>>::burn_item())]238 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {239 let caller = T::CrossAccountId::from_eth(caller);240 let token = token_id.try_into()?;241242 <Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;243 Ok(())244 }245}246247#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]248impl<T: Config> NonfungibleHandle<T> {249 fn minting_finished(&self) -> Result<bool> {250 Ok(false)251 }252253 /// `token_id` should be obtained with `next_token_id` method,254 /// unlike standard, you can't specify it manually255 #[weight(<SelfWeightOf<T>>::create_item())]256 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {257 let caller = T::CrossAccountId::from_eth(caller);258 let to = T::CrossAccountId::from_eth(to);259 let token_id: u32 = token_id.try_into()?;260 let budget = self261 .recorder262 .weight_calls_budget(<StructureWeight<T>>::find_parent());263264 if <TokensMinted<T>>::get(self.id)265 .checked_add(1)266 .ok_or("item id overflow")?267 != token_id268 {269 return Err("item id should be next".into());270 }271272 <Pallet<T>>::create_item(273 self,274 &caller,275 CreateItemData::<T> {276 const_data: BoundedVec::default(),277 properties: BoundedVec::default(),278 owner: to,279 },280 &budget,281 )282 .map_err(dispatch_to_evm::<T>)?;283284 Ok(true)285 }286287 /// `token_id` should be obtained with `next_token_id` method,288 /// unlike standard, you can't specify it manually289 #[solidity(rename_selector = "mintWithTokenURI")]290 #[weight(<SelfWeightOf<T>>::create_item())]291 fn mint_with_token_uri(292 &mut self,293 caller: caller,294 to: address,295 token_id: uint256,296 token_uri: string,297 ) -> Result<bool> {298 if !matches!(self.schema_version, SchemaVersion::ImageURL) {299 return Err(error_unsupported_schema_version());300 }301302 let caller = T::CrossAccountId::from_eth(caller);303 let to = T::CrossAccountId::from_eth(to);304 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;305 let budget = self306 .recorder307 .weight_calls_budget(<StructureWeight<T>>::find_parent());308309 if <TokensMinted<T>>::get(self.id)310 .checked_add(1)311 .ok_or("item id overflow")?312 != token_id313 {314 return Err("item id should be next".into());315 }316317 <Pallet<T>>::create_item(318 self,319 &caller,320 CreateItemData::<T> {321 const_data: Vec::<u8>::from(token_uri)322 .try_into()323 .map_err(|_| "token uri is too long")?,324 properties: BoundedVec::default(),325 owner: to,326 },327 &budget,328 )329 .map_err(dispatch_to_evm::<T>)?;330 Ok(true)331 }332333 /// Not implemented334 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {335 Err("not implementable".into())336 }337}338339#[solidity_interface(name = "ERC721UniqueExtensions")]340impl<T: Config> NonfungibleHandle<T> {341 #[weight(<SelfWeightOf<T>>::transfer())]342 fn transfer(343 &mut self,344 caller: caller,345 to: address,346 token_id: uint256,347 _value: value,348 ) -> Result<void> {349 let caller = T::CrossAccountId::from_eth(caller);350 let to = T::CrossAccountId::from_eth(to);351 let token = token_id.try_into()?;352 let budget = self353 .recorder354 .weight_calls_budget(<StructureWeight<T>>::find_parent());355356 <Pallet<T>>::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::<T>)?;357 Ok(())358 }359360 #[weight(<SelfWeightOf<T>>::burn_from())]361 fn burn_from(362 &mut self,363 caller: caller,364 from: address,365 token_id: uint256,366 _value: value,367 ) -> Result<void> {368 let caller = T::CrossAccountId::from_eth(caller);369 let from = T::CrossAccountId::from_eth(from);370 let token = token_id.try_into()?;371 let budget = self372 .recorder373 .weight_calls_budget(<StructureWeight<T>>::find_parent());374375 <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)376 .map_err(dispatch_to_evm::<T>)?;377 Ok(())378 }379380 fn next_token_id(&self) -> Result<uint256> {381 self.consume_store_reads(1)?;382 Ok(<TokensMinted<T>>::get(self.id)383 .checked_add(1)384 .ok_or("item id overflow")?385 .into())386 }387388 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]389 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {390 let caller = T::CrossAccountId::from_eth(caller);391 let to = T::CrossAccountId::from_eth(to);392 let mut expected_index = <TokensMinted<T>>::get(self.id)393 .checked_add(1)394 .ok_or("item id overflow")?;395 let budget = self396 .recorder397 .weight_calls_budget(<StructureWeight<T>>::find_parent());398399 let total_tokens = token_ids.len();400 for id in token_ids.into_iter() {401 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;402 if id != expected_index {403 return Err("item id should be next".into());404 }405 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;406 }407 let data = (0..total_tokens)408 .map(|_| CreateItemData::<T> {409 const_data: BoundedVec::default(),410 properties: BoundedVec::default(),411 owner: to.clone(),412 })413 .collect();414415 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)416 .map_err(dispatch_to_evm::<T>)?;417 Ok(true)418 }419420 #[solidity(rename_selector = "mintBulkWithTokenURI")]421 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]422 fn mint_bulk_with_token_uri(423 &mut self,424 caller: caller,425 to: address,426 tokens: Vec<(uint256, string)>,427 ) -> Result<bool> {428 if !matches!(self.schema_version, SchemaVersion::ImageURL) {429 return Err(error_unsupported_schema_version());430 }431432 let caller = T::CrossAccountId::from_eth(caller);433 let to = T::CrossAccountId::from_eth(to);434 let mut expected_index = <TokensMinted<T>>::get(self.id)435 .checked_add(1)436 .ok_or("item id overflow")?;437 let budget = self438 .recorder439 .weight_calls_budget(<StructureWeight<T>>::find_parent());440441 let mut data = Vec::with_capacity(tokens.len());442 for (id, token_uri) in tokens {443 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;444 if id != expected_index {445 return Err("item id should be next".into());446 }447 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;448449 data.push(CreateItemData::<T> {450 const_data: Vec::<u8>::from(token_uri)451 .try_into()452 .map_err(|_| "token uri is too long")?,453 properties: BoundedVec::default(),454 owner: to.clone(),455 });456 }457458 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)459 .map_err(dispatch_to_evm::<T>)?;460 Ok(true)461 }462}463464#[solidity_interface(465 name = "UniqueNFT",466 is(467 ERC721,468 ERC721Metadata,469 ERC721Enumerable,470 ERC721UniqueExtensions,471 ERC721Mintable,472 ERC721Burnable,473 via("CollectionHandle<T>", common_mut, CollectionProperties)474 )475)]476impl<T: Config> NonfungibleHandle<T> {}477478// Not a tests, but code generators479generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);480generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);481482impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {483 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");484485 fn call(self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileResult> {486 call::<T, UniqueNFTCall<T>, _>(*source, self, value, input)487 }488}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617extern crate alloc;18use core::{19 char::{REPLACEMENT_CHARACTER, decode_utf16},20 convert::TryInto,21};22use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};23use frame_support::BoundedVec;24use up_data_structs::{TokenId, SchemaVersion, PropertyPermission, PropertyKeyPermission, Property};25use pallet_evm_coder_substrate::dispatch_to_evm;26use sp_core::{H160, U256};27use sp_std::vec::Vec;28use pallet_common::{29 erc::{CommonEvmHandler, PrecompileResult, CollectionPropertiesCall},30 CollectionHandle,31};32use pallet_evm::account::CrossAccountId;33use pallet_evm_coder_substrate::call;34use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};3536use crate::{37 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,38 SelfWeightOf, weights::WeightInfo, TokenProperties,39};4041#[solidity_interface(name = "TokenProperties")]42impl<T: Config> NonfungibleHandle<T> {43 fn set_token_property_permission(44 &mut self,45 caller: caller,46 key: string,47 is_mutable: bool,48 collection_admin: bool,49 token_owner: bool,50 ) -> Result<()> {51 let caller = T::CrossAccountId::from_eth(caller);52 <Pallet<T>>::set_property_permission(53 self,54 &caller,55 PropertyKeyPermission {56 key: <Vec<u8>>::from(key)57 .try_into()58 .map_err(|_| "too long key")?,59 permission: PropertyPermission {60 mutable: is_mutable,61 collection_admin,62 token_owner,63 },64 },65 )66 .map_err(dispatch_to_evm::<T>)67 }6869 fn set_property(70 &mut self,71 caller: caller,72 token_id: uint256,73 key: string,74 value: bytes,75 ) -> Result<()> {76 let caller = T::CrossAccountId::from_eth(caller);77 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;78 let key = <Vec<u8>>::from(key)79 .try_into()80 .map_err(|_| "key too long")?;81 let value = value.try_into().map_err(|_| "value too long")?;8283 <Pallet<T>>::set_token_property(self, &caller, TokenId(token_id), Property { key, value })84 .map_err(dispatch_to_evm::<T>)85 }8687 fn delete_property(&mut self, token_id: uint256, caller: caller, key: string) -> Result<()> {88 let caller = T::CrossAccountId::from_eth(caller);89 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;90 let key = <Vec<u8>>::from(key)91 .try_into()92 .map_err(|_| "key too long")?;9394 <Pallet<T>>::delete_token_property(self, &caller, TokenId(token_id), key)95 .map_err(dispatch_to_evm::<T>)96 }9798 /// Throws error if key not found99 fn property(&self, token_id: uint256, key: string) -> Result<bytes> {100 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;101 let key = <Vec<u8>>::from(key)102 .try_into()103 .map_err(|_| "key too long")?;104105 let props = <TokenProperties<T>>::get((self.id, token_id));106 let prop = props.get(&key).ok_or("key not found")?;107108 Ok(prop.to_vec())109 }110}111112fn error_unsupported_schema_version() -> Error {113 alloc::format!(114 "Unsupported schema version! Support only {:?}",115 SchemaVersion::ImageURL116 )117 .as_str()118 .into()119}120121#[derive(ToLog)]122pub enum ERC721Events {123 Transfer {124 #[indexed]125 from: address,126 #[indexed]127 to: address,128 #[indexed]129 token_id: uint256,130 },131 Approval {132 #[indexed]133 owner: address,134 #[indexed]135 approved: address,136 #[indexed]137 token_id: uint256,138 },139 #[allow(dead_code)]140 ApprovalForAll {141 #[indexed]142 owner: address,143 #[indexed]144 operator: address,145 approved: bool,146 },147}148149#[derive(ToLog)]150pub enum ERC721MintableEvents {151 #[allow(dead_code)]152 MintingFinished {},153}154155#[solidity_interface(name = "ERC721Metadata")]156impl<T: Config> NonfungibleHandle<T> {157 fn name(&self) -> Result<string> {158 Ok(decode_utf16(self.name.iter().copied())159 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))160 .collect::<string>())161 }162163 fn symbol(&self) -> Result<string> {164 Ok(string::from_utf8_lossy(&self.token_prefix).into())165 }166167 /// Returns token's const_metadata168 #[solidity(rename_selector = "tokenURI")]169 fn token_uri(&self, token_id: uint256) -> Result<string> {170 if !matches!(self.schema_version, SchemaVersion::ImageURL) {171 return Err(error_unsupported_schema_version());172 }173174 self.consume_store_reads(1)?;175 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;176 Ok(string::from_utf8_lossy(177 &<TokenData<T>>::get((self.id, token_id))178 .ok_or("token not found")?179 .const_data,180 )181 .into())182 }183}184185#[solidity_interface(name = "ERC721Enumerable")]186impl<T: Config> NonfungibleHandle<T> {187 fn token_by_index(&self, index: uint256) -> Result<uint256> {188 Ok(index)189 }190191 /// Not implemented192 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {193 // TODO: Not implemetable194 Err("not implemented".into())195 }196197 fn total_supply(&self) -> Result<uint256> {198 self.consume_store_reads(1)?;199 Ok(<Pallet<T>>::total_supply(self).into())200 }201}202203#[solidity_interface(name = "ERC721", events(ERC721Events))]204impl<T: Config> NonfungibleHandle<T> {205 fn balance_of(&self, owner: address) -> Result<uint256> {206 self.consume_store_reads(1)?;207 let owner = T::CrossAccountId::from_eth(owner);208 let balance = <AccountBalance<T>>::get((self.id, owner));209 Ok(balance.into())210 }211 fn owner_of(&self, token_id: uint256) -> Result<address> {212 self.consume_store_reads(1)?;213 let token: TokenId = token_id.try_into()?;214 Ok(*<TokenData<T>>::get((self.id, token))215 .ok_or("token not found")?216 .owner217 .as_eth())218 }219 /// Not implemented220 fn safe_transfer_from_with_data(221 &mut self,222 _from: address,223 _to: address,224 _token_id: uint256,225 _data: bytes,226 _value: value,227 ) -> Result<void> {228 // TODO: Not implemetable229 Err("not implemented".into())230 }231 /// Not implemented232 fn safe_transfer_from(233 &mut self,234 _from: address,235 _to: address,236 _token_id: uint256,237 _value: value,238 ) -> Result<void> {239 // TODO: Not implemetable240 Err("not implemented".into())241 }242243 #[weight(<SelfWeightOf<T>>::transfer_from())]244 fn transfer_from(245 &mut self,246 caller: caller,247 from: address,248 to: address,249 token_id: uint256,250 _value: value,251 ) -> Result<void> {252 let caller = T::CrossAccountId::from_eth(caller);253 let from = T::CrossAccountId::from_eth(from);254 let to = T::CrossAccountId::from_eth(to);255 let token = token_id.try_into()?;256 let budget = self257 .recorder258 .weight_calls_budget(<StructureWeight<T>>::find_parent());259260 <Pallet<T>>::transfer_from(self, &caller, &from, &to, token, &budget)261 .map_err(dispatch_to_evm::<T>)?;262 Ok(())263 }264265 #[weight(<SelfWeightOf<T>>::approve())]266 fn approve(267 &mut self,268 caller: caller,269 approved: address,270 token_id: uint256,271 _value: value,272 ) -> Result<void> {273 let caller = T::CrossAccountId::from_eth(caller);274 let approved = T::CrossAccountId::from_eth(approved);275 let token = token_id.try_into()?;276277 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))278 .map_err(dispatch_to_evm::<T>)?;279 Ok(())280 }281282 /// Not implemented283 fn set_approval_for_all(284 &mut self,285 _caller: caller,286 _operator: address,287 _approved: bool,288 ) -> Result<void> {289 // TODO: Not implemetable290 Err("not implemented".into())291 }292293 /// Not implemented294 fn get_approved(&self, _token_id: uint256) -> Result<address> {295 // TODO: Not implemetable296 Err("not implemented".into())297 }298299 /// Not implemented300 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {301 // TODO: Not implemetable302 Err("not implemented".into())303 }304}305306#[solidity_interface(name = "ERC721Burnable")]307impl<T: Config> NonfungibleHandle<T> {308 #[weight(<SelfWeightOf<T>>::burn_item())]309 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {310 let caller = T::CrossAccountId::from_eth(caller);311 let token = token_id.try_into()?;312313 <Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;314 Ok(())315 }316}317318#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]319impl<T: Config> NonfungibleHandle<T> {320 fn minting_finished(&self) -> Result<bool> {321 Ok(false)322 }323324 /// `token_id` should be obtained with `next_token_id` method,325 /// unlike standard, you can't specify it manually326 #[weight(<SelfWeightOf<T>>::create_item())]327 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {328 let caller = T::CrossAccountId::from_eth(caller);329 let to = T::CrossAccountId::from_eth(to);330 let token_id: u32 = token_id.try_into()?;331 let budget = self332 .recorder333 .weight_calls_budget(<StructureWeight<T>>::find_parent());334335 if <TokensMinted<T>>::get(self.id)336 .checked_add(1)337 .ok_or("item id overflow")?338 != token_id339 {340 return Err("item id should be next".into());341 }342343 <Pallet<T>>::create_item(344 self,345 &caller,346 CreateItemData::<T> {347 const_data: BoundedVec::default(),348 properties: BoundedVec::default(),349 owner: to,350 },351 &budget,352 )353 .map_err(dispatch_to_evm::<T>)?;354355 Ok(true)356 }357358 /// `token_id` should be obtained with `next_token_id` method,359 /// unlike standard, you can't specify it manually360 #[solidity(rename_selector = "mintWithTokenURI")]361 #[weight(<SelfWeightOf<T>>::create_item())]362 fn mint_with_token_uri(363 &mut self,364 caller: caller,365 to: address,366 token_id: uint256,367 token_uri: string,368 ) -> Result<bool> {369 if !matches!(self.schema_version, SchemaVersion::ImageURL) {370 return Err(error_unsupported_schema_version());371 }372373 let caller = T::CrossAccountId::from_eth(caller);374 let to = T::CrossAccountId::from_eth(to);375 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;376 let budget = self377 .recorder378 .weight_calls_budget(<StructureWeight<T>>::find_parent());379380 if <TokensMinted<T>>::get(self.id)381 .checked_add(1)382 .ok_or("item id overflow")?383 != token_id384 {385 return Err("item id should be next".into());386 }387388 <Pallet<T>>::create_item(389 self,390 &caller,391 CreateItemData::<T> {392 const_data: Vec::<u8>::from(token_uri)393 .try_into()394 .map_err(|_| "token uri is too long")?,395 properties: BoundedVec::default(),396 owner: to,397 },398 &budget,399 )400 .map_err(dispatch_to_evm::<T>)?;401 Ok(true)402 }403404 /// Not implemented405 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {406 Err("not implementable".into())407 }408}409410#[solidity_interface(name = "ERC721UniqueExtensions")]411impl<T: Config> NonfungibleHandle<T> {412 #[weight(<SelfWeightOf<T>>::transfer())]413 fn transfer(414 &mut self,415 caller: caller,416 to: address,417 token_id: uint256,418 _value: value,419 ) -> Result<void> {420 let caller = T::CrossAccountId::from_eth(caller);421 let to = T::CrossAccountId::from_eth(to);422 let token = token_id.try_into()?;423 let budget = self424 .recorder425 .weight_calls_budget(<StructureWeight<T>>::find_parent());426427 <Pallet<T>>::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::<T>)?;428 Ok(())429 }430431 #[weight(<SelfWeightOf<T>>::burn_from())]432 fn burn_from(433 &mut self,434 caller: caller,435 from: address,436 token_id: uint256,437 _value: value,438 ) -> Result<void> {439 let caller = T::CrossAccountId::from_eth(caller);440 let from = T::CrossAccountId::from_eth(from);441 let token = token_id.try_into()?;442 let budget = self443 .recorder444 .weight_calls_budget(<StructureWeight<T>>::find_parent());445446 <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)447 .map_err(dispatch_to_evm::<T>)?;448 Ok(())449 }450451 fn next_token_id(&self) -> Result<uint256> {452 self.consume_store_reads(1)?;453 Ok(<TokensMinted<T>>::get(self.id)454 .checked_add(1)455 .ok_or("item id overflow")?456 .into())457 }458459 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]460 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {461 let caller = T::CrossAccountId::from_eth(caller);462 let to = T::CrossAccountId::from_eth(to);463 let mut expected_index = <TokensMinted<T>>::get(self.id)464 .checked_add(1)465 .ok_or("item id overflow")?;466 let budget = self467 .recorder468 .weight_calls_budget(<StructureWeight<T>>::find_parent());469470 let total_tokens = token_ids.len();471 for id in token_ids.into_iter() {472 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;473 if id != expected_index {474 return Err("item id should be next".into());475 }476 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;477 }478 let data = (0..total_tokens)479 .map(|_| CreateItemData::<T> {480 const_data: BoundedVec::default(),481 properties: BoundedVec::default(),482 owner: to.clone(),483 })484 .collect();485486 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)487 .map_err(dispatch_to_evm::<T>)?;488 Ok(true)489 }490491 #[solidity(rename_selector = "mintBulkWithTokenURI")]492 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]493 fn mint_bulk_with_token_uri(494 &mut self,495 caller: caller,496 to: address,497 tokens: Vec<(uint256, string)>,498 ) -> Result<bool> {499 if !matches!(self.schema_version, SchemaVersion::ImageURL) {500 return Err(error_unsupported_schema_version());501 }502503 let caller = T::CrossAccountId::from_eth(caller);504 let to = T::CrossAccountId::from_eth(to);505 let mut expected_index = <TokensMinted<T>>::get(self.id)506 .checked_add(1)507 .ok_or("item id overflow")?;508 let budget = self509 .recorder510 .weight_calls_budget(<StructureWeight<T>>::find_parent());511512 let mut data = Vec::with_capacity(tokens.len());513 for (id, token_uri) in tokens {514 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;515 if id != expected_index {516 return Err("item id should be next".into());517 }518 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;519520 data.push(CreateItemData::<T> {521 const_data: Vec::<u8>::from(token_uri)522 .try_into()523 .map_err(|_| "token uri is too long")?,524 properties: BoundedVec::default(),525 owner: to.clone(),526 });527 }528529 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)530 .map_err(dispatch_to_evm::<T>)?;531 Ok(true)532 }533}534535#[solidity_interface(536 name = "UniqueNFT",537 is(538 ERC721,539 ERC721Metadata,540 ERC721Enumerable,541 ERC721UniqueExtensions,542 ERC721Mintable,543 ERC721Burnable,544 via("CollectionHandle<T>", common_mut, CollectionProperties),545 TokenProperties,546 )547)]548impl<T: Config> NonfungibleHandle<T> {}549550// Not a tests, but code generators551generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);552generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);553554impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {555 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");556557 fn call(self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileResult> {558 call::<T, UniqueNFTCall<T>, _>(*source, self, value, input)559 }560}pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- 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 @@
<PalletCommon<T>>::set_property_permissions(collection, sender, property_permissions)
}
+ pub fn set_property_permission(
+ collection: &CollectionHandle<T>,
+ sender: &T::CrossAccountId,
+ permission: PropertyKeyPermission,
+ ) -> DispatchResult {
+ <PalletCommon<T>>::set_property_permission(collection, sender, permission)
+ }
+
pub fn transfer(
collection: &NonfungibleHandle<T>,
from: &T::CrossAccountId,
pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- 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
{}
tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth--- 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,
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- 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
{}