difftreelog
cargo fmt
in: master
3 files changed
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::{25 TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey,26 CollectionPropertiesVec,27};28use pallet_evm_coder_substrate::dispatch_to_evm;29use sp_std::vec::Vec;30use pallet_common::{31 erc::{CommonEvmHandler, PrecompileResult, CollectionCall, token_uri_key},32 CollectionHandle, CollectionPropertyPermissions,33};34use pallet_evm::{account::CrossAccountId, PrecompileHandle};35use pallet_evm_coder_substrate::call;36use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};3738use crate::{39 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,40 SelfWeightOf, weights::WeightInfo, TokenProperties,41 property_guard::PropertyGuard,42};4344#[solidity_interface(name = "TokenProperties")]45impl<T: Config> NonfungibleHandle<T> {46 fn set_token_property_permission(47 &mut self,48 caller: caller,49 key: string,50 is_mutable: bool,51 collection_admin: bool,52 token_owner: bool,53 ) -> Result<()> {54 let caller = T::CrossAccountId::from_eth(caller);55 <Pallet<T>>::set_property_permission(56 self,57 &caller,58 PropertyKeyPermission {59 key: <Vec<u8>>::from(key)60 .try_into()61 .map_err(|_| "too long key")?,62 permission: PropertyPermission {63 mutable: is_mutable,64 collection_admin,65 token_owner,66 },67 },68 )69 .map_err(dispatch_to_evm::<T>)70 }7172 fn set_property(73 &mut self,74 caller: caller,75 token_id: uint256,76 key: string,77 value: bytes,78 ) -> Result<()> {79 let caller = T::CrossAccountId::from_eth(caller);80 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;81 let key = <Vec<u8>>::from(key)82 .try_into()83 .map_err(|_| "key too long")?;84 let value = value.try_into().map_err(|_| "value too long")?;8586 let is_token_create = false;87 let budget = self88 .recorder89 .weight_calls_budget(<StructureWeight<T>>::find_parent());9091 let mut guard = PropertyGuard::new(92 &caller,93 self,94 TokenId(token_id),95 is_token_create,96 &budget,97 );9899 <Pallet<T>>::set_token_property(Property { key, value }, &mut guard)100 .map_err(dispatch_to_evm::<T>)101 }102103 fn delete_property(&mut self, token_id: uint256, caller: caller, key: string) -> Result<()> {104 let caller = T::CrossAccountId::from_eth(caller);105 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;106 let key = <Vec<u8>>::from(key)107 .try_into()108 .map_err(|_| "key too long")?;109110 let is_token_create = false;111 let budget = self112 .recorder113 .weight_calls_budget(<StructureWeight<T>>::find_parent());114115 let mut guard = PropertyGuard::new(116 &caller,117 self,118 TokenId(token_id),119 is_token_create,120 &budget,121 );122123 <Pallet<T>>::delete_token_property(key, &mut guard)124 .map_err(dispatch_to_evm::<T>)125 }126127 /// Throws error if key not found128 fn property(&self, token_id: uint256, key: string) -> Result<bytes> {129 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;130 let key = <Vec<u8>>::from(key)131 .try_into()132 .map_err(|_| "key too long")?;133134 let props = <TokenProperties<T>>::get((self.id, token_id));135 let prop = props.get(&key).ok_or("key not found")?;136137 Ok(prop.to_vec())138 }139}140141#[derive(ToLog)]142pub enum ERC721Events {143 Transfer {144 #[indexed]145 from: address,146 #[indexed]147 to: address,148 #[indexed]149 token_id: uint256,150 },151 Approval {152 #[indexed]153 owner: address,154 #[indexed]155 approved: address,156 #[indexed]157 token_id: uint256,158 },159 #[allow(dead_code)]160 ApprovalForAll {161 #[indexed]162 owner: address,163 #[indexed]164 operator: address,165 approved: bool,166 },167}168169#[derive(ToLog)]170pub enum ERC721MintableEvents {171 #[allow(dead_code)]172 MintingFinished {},173}174175#[solidity_interface(name = "ERC721Metadata")]176impl<T: Config> NonfungibleHandle<T> {177 fn name(&self) -> Result<string> {178 Ok(decode_utf16(self.name.iter().copied())179 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))180 .collect::<string>())181 }182183 fn symbol(&self) -> Result<string> {184 Ok(string::from_utf8_lossy(&self.token_prefix).into())185 }186187 /// Returns token's const_metadata188 #[solidity(rename_selector = "tokenURI")]189 fn token_uri(&self, token_id: uint256) -> Result<string> {190 let key = token_uri_key();191 if !has_token_permission::<T>(self.id, &key) {192 return Err("No tokenURI permission".into());193 }194195 self.consume_store_reads(1)?;196 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;197198 let properties = <TokenProperties<T>>::try_get((self.id, token_id))199 .map_err(|_| Error::Revert("Token properties not found".into()))?;200 if let Some(property) = properties.get(&key) {201 return Ok(string::from_utf8_lossy(property).into());202 }203204 Err("Property tokenURI not found".into())205 }206}207208#[solidity_interface(name = "ERC721Enumerable")]209impl<T: Config> NonfungibleHandle<T> {210 fn token_by_index(&self, index: uint256) -> Result<uint256> {211 Ok(index)212 }213214 /// Not implemented215 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {216 // TODO: Not implemetable217 Err("not implemented".into())218 }219220 fn total_supply(&self) -> Result<uint256> {221 self.consume_store_reads(1)?;222 Ok(<Pallet<T>>::total_supply(self).into())223 }224}225226#[solidity_interface(name = "ERC721", events(ERC721Events))]227impl<T: Config> NonfungibleHandle<T> {228 fn balance_of(&self, owner: address) -> Result<uint256> {229 self.consume_store_reads(1)?;230 let owner = T::CrossAccountId::from_eth(owner);231 let balance = <AccountBalance<T>>::get((self.id, owner));232 Ok(balance.into())233 }234 fn owner_of(&self, token_id: uint256) -> Result<address> {235 self.consume_store_reads(1)?;236 let token: TokenId = token_id.try_into()?;237 Ok(*<TokenData<T>>::get((self.id, token))238 .ok_or("token not found")?239 .owner240 .as_eth())241 }242 /// Not implemented243 fn safe_transfer_from_with_data(244 &mut self,245 _from: address,246 _to: address,247 _token_id: uint256,248 _data: bytes,249 _value: value,250 ) -> Result<void> {251 // TODO: Not implemetable252 Err("not implemented".into())253 }254 /// Not implemented255 fn safe_transfer_from(256 &mut self,257 _from: address,258 _to: address,259 _token_id: uint256,260 _value: value,261 ) -> Result<void> {262 // TODO: Not implemetable263 Err("not implemented".into())264 }265266 #[weight(<SelfWeightOf<T>>::transfer_from())]267 fn transfer_from(268 &mut self,269 caller: caller,270 from: address,271 to: address,272 token_id: uint256,273 _value: value,274 ) -> Result<void> {275 let caller = T::CrossAccountId::from_eth(caller);276 let from = T::CrossAccountId::from_eth(from);277 let to = T::CrossAccountId::from_eth(to);278 let token = token_id.try_into()?;279 let budget = self280 .recorder281 .weight_calls_budget(<StructureWeight<T>>::find_parent());282283 <Pallet<T>>::transfer_from(self, &caller, &from, &to, token, &budget)284 .map_err(dispatch_to_evm::<T>)?;285 Ok(())286 }287288 #[weight(<SelfWeightOf<T>>::approve())]289 fn approve(290 &mut self,291 caller: caller,292 approved: address,293 token_id: uint256,294 _value: value,295 ) -> Result<void> {296 let caller = T::CrossAccountId::from_eth(caller);297 let approved = T::CrossAccountId::from_eth(approved);298 let token = token_id.try_into()?;299300 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))301 .map_err(dispatch_to_evm::<T>)?;302 Ok(())303 }304305 /// Not implemented306 fn set_approval_for_all(307 &mut self,308 _caller: caller,309 _operator: address,310 _approved: bool,311 ) -> Result<void> {312 // TODO: Not implemetable313 Err("not implemented".into())314 }315316 /// Not implemented317 fn get_approved(&self, _token_id: uint256) -> Result<address> {318 // TODO: Not implemetable319 Err("not implemented".into())320 }321322 /// Not implemented323 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {324 // TODO: Not implemetable325 Err("not implemented".into())326 }327}328329#[solidity_interface(name = "ERC721Burnable")]330impl<T: Config> NonfungibleHandle<T> {331 #[weight(<SelfWeightOf<T>>::burn_item())]332 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {333 let caller = T::CrossAccountId::from_eth(caller);334 let token = token_id.try_into()?;335336 <Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;337 Ok(())338 }339}340341#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]342impl<T: Config> NonfungibleHandle<T> {343 fn minting_finished(&self) -> Result<bool> {344 Ok(false)345 }346347 /// `token_id` should be obtained with `next_token_id` method,348 /// unlike standard, you can't specify it manually349 #[weight(<SelfWeightOf<T>>::create_item())]350 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {351 let caller = T::CrossAccountId::from_eth(caller);352 let to = T::CrossAccountId::from_eth(to);353 let token_id: u32 = token_id.try_into()?;354 let budget = self355 .recorder356 .weight_calls_budget(<StructureWeight<T>>::find_parent());357358 if <TokensMinted<T>>::get(self.id)359 .checked_add(1)360 .ok_or("item id overflow")?361 != token_id362 {363 return Err("item id should be next".into());364 }365366 <Pallet<T>>::create_item(367 self,368 &caller,369 CreateItemData::<T> {370 properties: BoundedVec::default(),371 owner: to,372 },373 &budget,374 )375 .map_err(dispatch_to_evm::<T>)?;376377 Ok(true)378 }379380 /// `token_id` should be obtained with `next_token_id` method,381 /// unlike standard, you can't specify it manually382 #[solidity(rename_selector = "mintWithTokenURI")]383 #[weight(<SelfWeightOf<T>>::create_item())]384 fn mint_with_token_uri(385 &mut self,386 caller: caller,387 to: address,388 token_id: uint256,389 token_uri: string,390 ) -> Result<bool> {391 let key = token_uri_key();392 let permission = get_token_permission::<T>(self.id, &key)?;393 if !permission.collection_admin {394 return Err("Operation is not allowed".into());395 }396397 let caller = T::CrossAccountId::from_eth(caller);398 let to = T::CrossAccountId::from_eth(to);399 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;400 let budget = self401 .recorder402 .weight_calls_budget(<StructureWeight<T>>::find_parent());403404 if <TokensMinted<T>>::get(self.id)405 .checked_add(1)406 .ok_or("item id overflow")?407 != token_id408 {409 return Err("item id should be next".into());410 }411412 let mut properties = CollectionPropertiesVec::default();413 properties414 .try_push(Property {415 key,416 value: token_uri417 .into_bytes()418 .try_into()419 .map_err(|_| "token uri is too long")?,420 })421 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;422423 <Pallet<T>>::create_item(424 self,425 &caller,426 CreateItemData::<T> {427 properties,428 owner: to,429 },430 &budget,431 )432 .map_err(dispatch_to_evm::<T>)?;433 Ok(true)434 }435436 /// Not implemented437 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {438 Err("not implementable".into())439 }440}441442fn get_token_permission<T: Config>(443 collection_id: CollectionId,444 key: &PropertyKey,445) -> Result<PropertyPermission> {446 let token_property_permissions = CollectionPropertyPermissions::<T>::try_get(collection_id)447 .map_err(|_| Error::Revert("No permissions for collection".into()))?;448 let a = token_property_permissions449 .get(key)450 .map(|p| p.clone())451 .ok_or_else(|| Error::Revert("No permission".into()))?;452 Ok(a)453}454455fn has_token_permission<T: Config>(collection_id: CollectionId, key: &PropertyKey) -> bool {456 if let Ok(token_property_permissions) =457 CollectionPropertyPermissions::<T>::try_get(collection_id)458 {459 return token_property_permissions.contains_key(key);460 }461462 false463}464465#[solidity_interface(name = "ERC721UniqueExtensions")]466impl<T: Config> NonfungibleHandle<T> {467 #[weight(<SelfWeightOf<T>>::transfer())]468 fn transfer(469 &mut self,470 caller: caller,471 to: address,472 token_id: uint256,473 _value: value,474 ) -> Result<void> {475 let caller = T::CrossAccountId::from_eth(caller);476 let to = T::CrossAccountId::from_eth(to);477 let token = token_id.try_into()?;478 let budget = self479 .recorder480 .weight_calls_budget(<StructureWeight<T>>::find_parent());481482 <Pallet<T>>::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::<T>)?;483 Ok(())484 }485486 #[weight(<SelfWeightOf<T>>::burn_from())]487 fn burn_from(488 &mut self,489 caller: caller,490 from: address,491 token_id: uint256,492 _value: value,493 ) -> Result<void> {494 let caller = T::CrossAccountId::from_eth(caller);495 let from = T::CrossAccountId::from_eth(from);496 let token = token_id.try_into()?;497 let budget = self498 .recorder499 .weight_calls_budget(<StructureWeight<T>>::find_parent());500501 <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)502 .map_err(dispatch_to_evm::<T>)?;503 Ok(())504 }505506 fn next_token_id(&self) -> Result<uint256> {507 self.consume_store_reads(1)?;508 Ok(<TokensMinted<T>>::get(self.id)509 .checked_add(1)510 .ok_or("item id overflow")?511 .into())512 }513514 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]515 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {516 let caller = T::CrossAccountId::from_eth(caller);517 let to = T::CrossAccountId::from_eth(to);518 let mut expected_index = <TokensMinted<T>>::get(self.id)519 .checked_add(1)520 .ok_or("item id overflow")?;521 let budget = self522 .recorder523 .weight_calls_budget(<StructureWeight<T>>::find_parent());524525 let total_tokens = token_ids.len();526 for id in token_ids.into_iter() {527 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;528 if id != expected_index {529 return Err("item id should be next".into());530 }531 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;532 }533 let data = (0..total_tokens)534 .map(|_| CreateItemData::<T> {535 properties: BoundedVec::default(),536 owner: to.clone(),537 })538 .collect();539540 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)541 .map_err(dispatch_to_evm::<T>)?;542 Ok(true)543 }544545 #[solidity(rename_selector = "mintBulkWithTokenURI")]546 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]547 fn mint_bulk_with_token_uri(548 &mut self,549 caller: caller,550 to: address,551 tokens: Vec<(uint256, string)>,552 ) -> Result<bool> {553 let key = token_uri_key();554 let caller = T::CrossAccountId::from_eth(caller);555 let to = T::CrossAccountId::from_eth(to);556 let mut expected_index = <TokensMinted<T>>::get(self.id)557 .checked_add(1)558 .ok_or("item id overflow")?;559 let budget = self560 .recorder561 .weight_calls_budget(<StructureWeight<T>>::find_parent());562563 let mut data = Vec::with_capacity(tokens.len());564 for (id, token_uri) in tokens {565 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;566 if id != expected_index {567 return Err("item id should be next".into());568 }569 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;570571 let mut properties = CollectionPropertiesVec::default();572 properties573 .try_push(Property {574 key: key.clone(),575 value: token_uri576 .into_bytes()577 .try_into()578 .map_err(|_| "token uri is too long")?,579 })580 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;581582 data.push(CreateItemData::<T> {583 properties,584 owner: to.clone(),585 });586 }587588 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)589 .map_err(dispatch_to_evm::<T>)?;590 Ok(true)591 }592}593594#[solidity_interface(595 name = "UniqueNFT",596 is(597 ERC721,598 ERC721Metadata,599 ERC721Enumerable,600 ERC721UniqueExtensions,601 ERC721Mintable,602 ERC721Burnable,603 via("CollectionHandle<T>", common_mut, Collection),604 TokenProperties,605 )606)]607impl<T: Config> NonfungibleHandle<T> where T::AccountId: From<[u8; 32]> {}608609// Not a tests, but code generators610generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);611generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);612613impl<T: Config> CommonEvmHandler for NonfungibleHandle<T>614where615 T::AccountId: From<[u8; 32]>,616{617 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");618619 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {620 call::<T, UniqueNFTCall<T>, _, _>(handle, self)621 }622}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::{25 TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey,26 CollectionPropertiesVec,27};28use pallet_evm_coder_substrate::dispatch_to_evm;29use sp_std::vec::Vec;30use pallet_common::{31 erc::{CommonEvmHandler, PrecompileResult, CollectionCall, token_uri_key},32 CollectionHandle, CollectionPropertyPermissions,33};34use pallet_evm::{account::CrossAccountId, PrecompileHandle};35use pallet_evm_coder_substrate::call;36use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};3738use crate::{39 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,40 SelfWeightOf, weights::WeightInfo, TokenProperties, property_guard::PropertyGuard,41};4243#[solidity_interface(name = "TokenProperties")]44impl<T: Config> NonfungibleHandle<T> {45 fn set_token_property_permission(46 &mut self,47 caller: caller,48 key: string,49 is_mutable: bool,50 collection_admin: bool,51 token_owner: bool,52 ) -> Result<()> {53 let caller = T::CrossAccountId::from_eth(caller);54 <Pallet<T>>::set_property_permission(55 self,56 &caller,57 PropertyKeyPermission {58 key: <Vec<u8>>::from(key)59 .try_into()60 .map_err(|_| "too long key")?,61 permission: PropertyPermission {62 mutable: is_mutable,63 collection_admin,64 token_owner,65 },66 },67 )68 .map_err(dispatch_to_evm::<T>)69 }7071 fn set_property(72 &mut self,73 caller: caller,74 token_id: uint256,75 key: string,76 value: bytes,77 ) -> Result<()> {78 let caller = T::CrossAccountId::from_eth(caller);79 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;80 let key = <Vec<u8>>::from(key)81 .try_into()82 .map_err(|_| "key too long")?;83 let value = value.try_into().map_err(|_| "value too long")?;8485 let is_token_create = false;86 let budget = self87 .recorder88 .weight_calls_budget(<StructureWeight<T>>::find_parent());8990 let mut guard =91 PropertyGuard::new(&caller, self, TokenId(token_id), is_token_create, &budget);9293 <Pallet<T>>::set_token_property(Property { key, value }, &mut guard)94 .map_err(dispatch_to_evm::<T>)95 }9697 fn delete_property(&mut self, token_id: uint256, caller: caller, key: string) -> Result<()> {98 let caller = T::CrossAccountId::from_eth(caller);99 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;100 let key = <Vec<u8>>::from(key)101 .try_into()102 .map_err(|_| "key too long")?;103104 let is_token_create = false;105 let budget = self106 .recorder107 .weight_calls_budget(<StructureWeight<T>>::find_parent());108109 let mut guard =110 PropertyGuard::new(&caller, self, TokenId(token_id), is_token_create, &budget);111112 <Pallet<T>>::delete_token_property(key, &mut guard).map_err(dispatch_to_evm::<T>)113 }114115 /// Throws error if key not found116 fn property(&self, token_id: uint256, key: string) -> Result<bytes> {117 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;118 let key = <Vec<u8>>::from(key)119 .try_into()120 .map_err(|_| "key too long")?;121122 let props = <TokenProperties<T>>::get((self.id, token_id));123 let prop = props.get(&key).ok_or("key not found")?;124125 Ok(prop.to_vec())126 }127}128129#[derive(ToLog)]130pub enum ERC721Events {131 Transfer {132 #[indexed]133 from: address,134 #[indexed]135 to: address,136 #[indexed]137 token_id: uint256,138 },139 Approval {140 #[indexed]141 owner: address,142 #[indexed]143 approved: address,144 #[indexed]145 token_id: uint256,146 },147 #[allow(dead_code)]148 ApprovalForAll {149 #[indexed]150 owner: address,151 #[indexed]152 operator: address,153 approved: bool,154 },155}156157#[derive(ToLog)]158pub enum ERC721MintableEvents {159 #[allow(dead_code)]160 MintingFinished {},161}162163#[solidity_interface(name = "ERC721Metadata")]164impl<T: Config> NonfungibleHandle<T> {165 fn name(&self) -> Result<string> {166 Ok(decode_utf16(self.name.iter().copied())167 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))168 .collect::<string>())169 }170171 fn symbol(&self) -> Result<string> {172 Ok(string::from_utf8_lossy(&self.token_prefix).into())173 }174175 /// Returns token's const_metadata176 #[solidity(rename_selector = "tokenURI")]177 fn token_uri(&self, token_id: uint256) -> Result<string> {178 let key = token_uri_key();179 if !has_token_permission::<T>(self.id, &key) {180 return Err("No tokenURI permission".into());181 }182183 self.consume_store_reads(1)?;184 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;185186 let properties = <TokenProperties<T>>::try_get((self.id, token_id))187 .map_err(|_| Error::Revert("Token properties not found".into()))?;188 if let Some(property) = properties.get(&key) {189 return Ok(string::from_utf8_lossy(property).into());190 }191192 Err("Property tokenURI not found".into())193 }194}195196#[solidity_interface(name = "ERC721Enumerable")]197impl<T: Config> NonfungibleHandle<T> {198 fn token_by_index(&self, index: uint256) -> Result<uint256> {199 Ok(index)200 }201202 /// Not implemented203 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {204 // TODO: Not implemetable205 Err("not implemented".into())206 }207208 fn total_supply(&self) -> Result<uint256> {209 self.consume_store_reads(1)?;210 Ok(<Pallet<T>>::total_supply(self).into())211 }212}213214#[solidity_interface(name = "ERC721", events(ERC721Events))]215impl<T: Config> NonfungibleHandle<T> {216 fn balance_of(&self, owner: address) -> Result<uint256> {217 self.consume_store_reads(1)?;218 let owner = T::CrossAccountId::from_eth(owner);219 let balance = <AccountBalance<T>>::get((self.id, owner));220 Ok(balance.into())221 }222 fn owner_of(&self, token_id: uint256) -> Result<address> {223 self.consume_store_reads(1)?;224 let token: TokenId = token_id.try_into()?;225 Ok(*<TokenData<T>>::get((self.id, token))226 .ok_or("token not found")?227 .owner228 .as_eth())229 }230 /// Not implemented231 fn safe_transfer_from_with_data(232 &mut self,233 _from: address,234 _to: address,235 _token_id: uint256,236 _data: bytes,237 _value: value,238 ) -> Result<void> {239 // TODO: Not implemetable240 Err("not implemented".into())241 }242 /// Not implemented243 fn safe_transfer_from(244 &mut self,245 _from: address,246 _to: address,247 _token_id: uint256,248 _value: value,249 ) -> Result<void> {250 // TODO: Not implemetable251 Err("not implemented".into())252 }253254 #[weight(<SelfWeightOf<T>>::transfer_from())]255 fn transfer_from(256 &mut self,257 caller: caller,258 from: address,259 to: address,260 token_id: uint256,261 _value: value,262 ) -> Result<void> {263 let caller = T::CrossAccountId::from_eth(caller);264 let from = T::CrossAccountId::from_eth(from);265 let to = T::CrossAccountId::from_eth(to);266 let token = token_id.try_into()?;267 let budget = self268 .recorder269 .weight_calls_budget(<StructureWeight<T>>::find_parent());270271 <Pallet<T>>::transfer_from(self, &caller, &from, &to, token, &budget)272 .map_err(dispatch_to_evm::<T>)?;273 Ok(())274 }275276 #[weight(<SelfWeightOf<T>>::approve())]277 fn approve(278 &mut self,279 caller: caller,280 approved: address,281 token_id: uint256,282 _value: value,283 ) -> Result<void> {284 let caller = T::CrossAccountId::from_eth(caller);285 let approved = T::CrossAccountId::from_eth(approved);286 let token = token_id.try_into()?;287288 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))289 .map_err(dispatch_to_evm::<T>)?;290 Ok(())291 }292293 /// Not implemented294 fn set_approval_for_all(295 &mut self,296 _caller: caller,297 _operator: address,298 _approved: bool,299 ) -> Result<void> {300 // TODO: Not implemetable301 Err("not implemented".into())302 }303304 /// Not implemented305 fn get_approved(&self, _token_id: uint256) -> Result<address> {306 // TODO: Not implemetable307 Err("not implemented".into())308 }309310 /// Not implemented311 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {312 // TODO: Not implemetable313 Err("not implemented".into())314 }315}316317#[solidity_interface(name = "ERC721Burnable")]318impl<T: Config> NonfungibleHandle<T> {319 #[weight(<SelfWeightOf<T>>::burn_item())]320 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {321 let caller = T::CrossAccountId::from_eth(caller);322 let token = token_id.try_into()?;323324 <Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;325 Ok(())326 }327}328329#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]330impl<T: Config> NonfungibleHandle<T> {331 fn minting_finished(&self) -> Result<bool> {332 Ok(false)333 }334335 /// `token_id` should be obtained with `next_token_id` method,336 /// unlike standard, you can't specify it manually337 #[weight(<SelfWeightOf<T>>::create_item())]338 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {339 let caller = T::CrossAccountId::from_eth(caller);340 let to = T::CrossAccountId::from_eth(to);341 let token_id: u32 = token_id.try_into()?;342 let budget = self343 .recorder344 .weight_calls_budget(<StructureWeight<T>>::find_parent());345346 if <TokensMinted<T>>::get(self.id)347 .checked_add(1)348 .ok_or("item id overflow")?349 != token_id350 {351 return Err("item id should be next".into());352 }353354 <Pallet<T>>::create_item(355 self,356 &caller,357 CreateItemData::<T> {358 properties: BoundedVec::default(),359 owner: to,360 },361 &budget,362 )363 .map_err(dispatch_to_evm::<T>)?;364365 Ok(true)366 }367368 /// `token_id` should be obtained with `next_token_id` method,369 /// unlike standard, you can't specify it manually370 #[solidity(rename_selector = "mintWithTokenURI")]371 #[weight(<SelfWeightOf<T>>::create_item())]372 fn mint_with_token_uri(373 &mut self,374 caller: caller,375 to: address,376 token_id: uint256,377 token_uri: string,378 ) -> Result<bool> {379 let key = token_uri_key();380 let permission = get_token_permission::<T>(self.id, &key)?;381 if !permission.collection_admin {382 return Err("Operation is not allowed".into());383 }384385 let caller = T::CrossAccountId::from_eth(caller);386 let to = T::CrossAccountId::from_eth(to);387 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;388 let budget = self389 .recorder390 .weight_calls_budget(<StructureWeight<T>>::find_parent());391392 if <TokensMinted<T>>::get(self.id)393 .checked_add(1)394 .ok_or("item id overflow")?395 != token_id396 {397 return Err("item id should be next".into());398 }399400 let mut properties = CollectionPropertiesVec::default();401 properties402 .try_push(Property {403 key,404 value: token_uri405 .into_bytes()406 .try_into()407 .map_err(|_| "token uri is too long")?,408 })409 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;410411 <Pallet<T>>::create_item(412 self,413 &caller,414 CreateItemData::<T> {415 properties,416 owner: to,417 },418 &budget,419 )420 .map_err(dispatch_to_evm::<T>)?;421 Ok(true)422 }423424 /// Not implemented425 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {426 Err("not implementable".into())427 }428}429430fn get_token_permission<T: Config>(431 collection_id: CollectionId,432 key: &PropertyKey,433) -> Result<PropertyPermission> {434 let token_property_permissions = CollectionPropertyPermissions::<T>::try_get(collection_id)435 .map_err(|_| Error::Revert("No permissions for collection".into()))?;436 let a = token_property_permissions437 .get(key)438 .map(|p| p.clone())439 .ok_or_else(|| Error::Revert("No permission".into()))?;440 Ok(a)441}442443fn has_token_permission<T: Config>(collection_id: CollectionId, key: &PropertyKey) -> bool {444 if let Ok(token_property_permissions) =445 CollectionPropertyPermissions::<T>::try_get(collection_id)446 {447 return token_property_permissions.contains_key(key);448 }449450 false451}452453#[solidity_interface(name = "ERC721UniqueExtensions")]454impl<T: Config> NonfungibleHandle<T> {455 #[weight(<SelfWeightOf<T>>::transfer())]456 fn transfer(457 &mut self,458 caller: caller,459 to: address,460 token_id: uint256,461 _value: value,462 ) -> Result<void> {463 let caller = T::CrossAccountId::from_eth(caller);464 let to = T::CrossAccountId::from_eth(to);465 let token = token_id.try_into()?;466 let budget = self467 .recorder468 .weight_calls_budget(<StructureWeight<T>>::find_parent());469470 <Pallet<T>>::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::<T>)?;471 Ok(())472 }473474 #[weight(<SelfWeightOf<T>>::burn_from())]475 fn burn_from(476 &mut self,477 caller: caller,478 from: address,479 token_id: uint256,480 _value: value,481 ) -> Result<void> {482 let caller = T::CrossAccountId::from_eth(caller);483 let from = T::CrossAccountId::from_eth(from);484 let token = token_id.try_into()?;485 let budget = self486 .recorder487 .weight_calls_budget(<StructureWeight<T>>::find_parent());488489 <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)490 .map_err(dispatch_to_evm::<T>)?;491 Ok(())492 }493494 fn next_token_id(&self) -> Result<uint256> {495 self.consume_store_reads(1)?;496 Ok(<TokensMinted<T>>::get(self.id)497 .checked_add(1)498 .ok_or("item id overflow")?499 .into())500 }501502 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]503 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {504 let caller = T::CrossAccountId::from_eth(caller);505 let to = T::CrossAccountId::from_eth(to);506 let mut expected_index = <TokensMinted<T>>::get(self.id)507 .checked_add(1)508 .ok_or("item id overflow")?;509 let budget = self510 .recorder511 .weight_calls_budget(<StructureWeight<T>>::find_parent());512513 let total_tokens = token_ids.len();514 for id in token_ids.into_iter() {515 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;516 if id != expected_index {517 return Err("item id should be next".into());518 }519 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;520 }521 let data = (0..total_tokens)522 .map(|_| CreateItemData::<T> {523 properties: BoundedVec::default(),524 owner: to.clone(),525 })526 .collect();527528 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)529 .map_err(dispatch_to_evm::<T>)?;530 Ok(true)531 }532533 #[solidity(rename_selector = "mintBulkWithTokenURI")]534 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]535 fn mint_bulk_with_token_uri(536 &mut self,537 caller: caller,538 to: address,539 tokens: Vec<(uint256, string)>,540 ) -> Result<bool> {541 let key = token_uri_key();542 let caller = T::CrossAccountId::from_eth(caller);543 let to = T::CrossAccountId::from_eth(to);544 let mut expected_index = <TokensMinted<T>>::get(self.id)545 .checked_add(1)546 .ok_or("item id overflow")?;547 let budget = self548 .recorder549 .weight_calls_budget(<StructureWeight<T>>::find_parent());550551 let mut data = Vec::with_capacity(tokens.len());552 for (id, token_uri) in tokens {553 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;554 if id != expected_index {555 return Err("item id should be next".into());556 }557 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;558559 let mut properties = CollectionPropertiesVec::default();560 properties561 .try_push(Property {562 key: key.clone(),563 value: token_uri564 .into_bytes()565 .try_into()566 .map_err(|_| "token uri is too long")?,567 })568 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;569570 data.push(CreateItemData::<T> {571 properties,572 owner: to.clone(),573 });574 }575576 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)577 .map_err(dispatch_to_evm::<T>)?;578 Ok(true)579 }580}581582#[solidity_interface(583 name = "UniqueNFT",584 is(585 ERC721,586 ERC721Metadata,587 ERC721Enumerable,588 ERC721UniqueExtensions,589 ERC721Mintable,590 ERC721Burnable,591 via("CollectionHandle<T>", common_mut, Collection),592 TokenProperties,593 )594)]595impl<T: Config> NonfungibleHandle<T> where T::AccountId: From<[u8; 32]> {}596597// Not a tests, but code generators598generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);599generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);600601impl<T: Config> CommonEvmHandler for NonfungibleHandle<T>602where603 T::AccountId: From<[u8; 32]>,604{605 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");606607 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {608 call::<T, UniqueNFTCall<T>, _, _>(handle, self)609 }610}pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -488,7 +488,10 @@
})
}
- pub fn set_token_property(property: Property, guard: &mut PropertyGuard<'_, T>) -> DispatchResult {
+ pub fn set_token_property(
+ property: Property,
+ guard: &mut PropertyGuard<'_, T>,
+ ) -> DispatchResult {
Self::check_token_change_permission(&property.key, guard)?;
<TokenProperties<T>>::try_mutate((guard.collection.id, guard.token), |properties| {
@@ -530,7 +533,10 @@
Ok(())
}
- pub fn delete_token_property(property_key: PropertyKey, guard: &mut PropertyGuard<'_, T>) -> DispatchResult {
+ pub fn delete_token_property(
+ property_key: PropertyKey,
+ guard: &mut PropertyGuard<'_, T>,
+ ) -> DispatchResult {
Self::check_token_change_permission(&property_key, guard)?;
<TokenProperties<T>>::try_mutate((guard.collection.id, guard.token), |properties| {
@@ -547,7 +553,10 @@
Ok(())
}
- fn check_token_change_permission(property_key: &PropertyKey, guard: &mut PropertyGuard<'_, T>) -> DispatchResult {
+ fn check_token_change_permission(
+ property_key: &PropertyKey,
+ guard: &mut PropertyGuard<'_, T>,
+ ) -> DispatchResult {
let permission = <PalletCommon<T>>::property_permissions(guard.collection.id)
.get(property_key)
.cloned()
pallets/nonfungible/src/property_guard.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/property_guard.rs
+++ b/pallets/nonfungible/src/property_guard.rs
@@ -1,63 +1,64 @@
use super::*;
pub struct PropertyGuard<'a, T: Config> {
- pub sender: &'a T::CrossAccountId,
- pub collection: &'a NonfungibleHandle<T>,
- pub token: TokenId,
- pub is_token_create: bool,
- budget: &'a dyn Budget,
+ pub sender: &'a T::CrossAccountId,
+ pub collection: &'a NonfungibleHandle<T>,
+ pub token: TokenId,
+ pub is_token_create: bool,
+ budget: &'a dyn Budget,
- collection_admin_result: Option<DispatchResult>,
- token_owner_result: Option<DispatchResult>,
+ collection_admin_result: Option<DispatchResult>,
+ token_owner_result: Option<DispatchResult>,
}
impl<'a, T: Config> PropertyGuard<'a, T> {
- pub fn new(
- sender: &'a T::CrossAccountId,
- collection: &'a NonfungibleHandle<T>,
- token: TokenId,
- is_token_create: bool,
- budget: &'a dyn Budget,
- ) -> Self {
- Self {
- sender,
- collection,
- token,
- is_token_create,
- budget,
+ pub fn new(
+ sender: &'a T::CrossAccountId,
+ collection: &'a NonfungibleHandle<T>,
+ token: TokenId,
+ is_token_create: bool,
+ budget: &'a dyn Budget,
+ ) -> Self {
+ Self {
+ sender,
+ collection,
+ token,
+ is_token_create,
+ budget,
- collection_admin_result: None,
- token_owner_result: None
- }
- }
+ collection_admin_result: None,
+ token_owner_result: None,
+ }
+ }
- pub fn check_collection_admin(&mut self) -> DispatchResult {
- if self.collection_admin_result.is_none() {
- self.collection_admin_result = Some(self.collection.check_is_owner_or_admin(self.sender));
- }
+ pub fn check_collection_admin(&mut self) -> DispatchResult {
+ if self.collection_admin_result.is_none() {
+ self.collection_admin_result =
+ Some(self.collection.check_is_owner_or_admin(self.sender));
+ }
- self.collection_admin_result.unwrap()
- }
+ self.collection_admin_result.unwrap()
+ }
- pub fn check_token_owner(&mut self) -> DispatchResult {
- if self.token_owner_result.is_none() {
- let is_owned = <PalletStructure<T>>::check_indirectly_owned(
- self.sender.clone(),
- self.collection.id,
- self.token,
- None,
- self.budget,
- )?;
+ pub fn check_token_owner(&mut self) -> DispatchResult {
+ if self.token_owner_result.is_none() {
+ let is_owned = <PalletStructure<T>>::check_indirectly_owned(
+ self.sender.clone(),
+ self.collection.id,
+ self.token,
+ None,
+ self.budget,
+ )?;
- let result = if is_owned {
- Ok(())
- } else {
- Err(<CommonError<T>>::NoPermission.into())
- };
+ let result = if is_owned {
+ Ok(())
+ } else {
+ Err(<CommonError<T>>::NoPermission.into())
+ };
- self.token_owner_result = Some(result);
- }
+ self.token_owner_result = Some(result);
+ }
- self.token_owner_result.unwrap()
- }
+ self.token_owner_result.unwrap()
+ }
}