difftreelog
fix revert set_allowance
in: master
8 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1298,7 +1298,6 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
- nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo;
fn transfer_from(
&self,
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -226,7 +226,6 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
- _nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo {
ensure!(
token == TokenId::default(),
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -308,15 +308,14 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
- nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo {
ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
with_weight(
if amount == 1 {
- <Pallet<T>>::set_allowance(self, &sender, token, Some(&spender), nesting_budget)
+ <Pallet<T>>::set_allowance(self, &sender, token, Some(&spender))
} else {
- <Pallet<T>>::set_allowance(self, &sender, token, None, nesting_budget)
+ <Pallet<T>>::set_allowance(self, &sender, token, None)
},
<CommonWeights<T>>::approve(),
)
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};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 <Pallet<T>>::set_token_property(self, &caller, TokenId(token_id), Property { key, value })86 .map_err(dispatch_to_evm::<T>)87 }8889 fn delete_property(&mut self, token_id: uint256, caller: caller, key: string) -> Result<()> {90 let caller = T::CrossAccountId::from_eth(caller);91 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;92 let key = <Vec<u8>>::from(key)93 .try_into()94 .map_err(|_| "key too long")?;9596 <Pallet<T>>::delete_token_property(self, &caller, TokenId(token_id), key)97 .map_err(dispatch_to_evm::<T>)98 }99100 /// Throws error if key not found101 fn property(&self, token_id: uint256, key: string) -> Result<bytes> {102 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;103 let key = <Vec<u8>>::from(key)104 .try_into()105 .map_err(|_| "key too long")?;106107 let props = <TokenProperties<T>>::get((self.id, token_id));108 let prop = props.get(&key).ok_or("key not found")?;109110 Ok(prop.to_vec())111 }112}113114#[derive(ToLog)]115pub enum ERC721Events {116 Transfer {117 #[indexed]118 from: address,119 #[indexed]120 to: address,121 #[indexed]122 token_id: uint256,123 },124 Approval {125 #[indexed]126 owner: address,127 #[indexed]128 approved: address,129 #[indexed]130 token_id: uint256,131 },132 #[allow(dead_code)]133 ApprovalForAll {134 #[indexed]135 owner: address,136 #[indexed]137 operator: address,138 approved: bool,139 },140}141142#[derive(ToLog)]143pub enum ERC721MintableEvents {144 #[allow(dead_code)]145 MintingFinished {},146}147148#[solidity_interface(name = "ERC721Metadata")]149impl<T: Config> NonfungibleHandle<T> {150 fn name(&self) -> Result<string> {151 Ok(decode_utf16(self.name.iter().copied())152 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))153 .collect::<string>())154 }155156 fn symbol(&self) -> Result<string> {157 Ok(string::from_utf8_lossy(&self.token_prefix).into())158 }159160 /// Returns token's const_metadata161 #[solidity(rename_selector = "tokenURI")]162 fn token_uri(&self, token_id: uint256) -> Result<string> {163 let key = token_uri_key();164 if !has_token_permission::<T>(self.id, &key) {165 return Err("No tokenURI permission".into());166 }167168 self.consume_store_reads(1)?;169 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;170171 let properties = <TokenProperties<T>>::try_get((self.id, token_id))172 .map_err(|_| Error::Revert("Token properties not found".into()))?;173 if let Some(property) = properties.get(&key) {174 return Ok(string::from_utf8_lossy(property).into());175 }176177 Err("Property tokenURI not found".into())178 }179}180181#[solidity_interface(name = "ERC721Enumerable")]182impl<T: Config> NonfungibleHandle<T> {183 fn token_by_index(&self, index: uint256) -> Result<uint256> {184 Ok(index)185 }186187 /// Not implemented188 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {189 // TODO: Not implemetable190 Err("not implemented".into())191 }192193 fn total_supply(&self) -> Result<uint256> {194 self.consume_store_reads(1)?;195 Ok(<Pallet<T>>::total_supply(self).into())196 }197}198199#[solidity_interface(name = "ERC721", events(ERC721Events))]200impl<T: Config> NonfungibleHandle<T> {201 fn balance_of(&self, owner: address) -> Result<uint256> {202 self.consume_store_reads(1)?;203 let owner = T::CrossAccountId::from_eth(owner);204 let balance = <AccountBalance<T>>::get((self.id, owner));205 Ok(balance.into())206 }207 fn owner_of(&self, token_id: uint256) -> Result<address> {208 self.consume_store_reads(1)?;209 let token: TokenId = token_id.try_into()?;210 Ok(*<TokenData<T>>::get((self.id, token))211 .ok_or("token not found")?212 .owner213 .as_eth())214 }215 /// Not implemented216 fn safe_transfer_from_with_data(217 &mut self,218 _from: address,219 _to: address,220 _token_id: uint256,221 _data: bytes,222 _value: value,223 ) -> Result<void> {224 // TODO: Not implemetable225 Err("not implemented".into())226 }227 /// Not implemented228 fn safe_transfer_from(229 &mut self,230 _from: address,231 _to: address,232 _token_id: uint256,233 _value: value,234 ) -> Result<void> {235 // TODO: Not implemetable236 Err("not implemented".into())237 }238239 #[weight(<SelfWeightOf<T>>::transfer_from())]240 fn transfer_from(241 &mut self,242 caller: caller,243 from: address,244 to: address,245 token_id: uint256,246 _value: value,247 ) -> Result<void> {248 let caller = T::CrossAccountId::from_eth(caller);249 let from = T::CrossAccountId::from_eth(from);250 let to = T::CrossAccountId::from_eth(to);251 let token = token_id.try_into()?;252 let budget = self253 .recorder254 .weight_calls_budget(<StructureWeight<T>>::find_parent());255256 <Pallet<T>>::transfer_from(self, &caller, &from, &to, token, &budget)257 .map_err(dispatch_to_evm::<T>)?;258 Ok(())259 }260261 #[weight(<SelfWeightOf<T>>::approve())]262 fn approve(263 &mut self,264 caller: caller,265 approved: address,266 token_id: uint256,267 _value: value,268 ) -> Result<void> {269 let caller = T::CrossAccountId::from_eth(caller);270 let approved = T::CrossAccountId::from_eth(approved);271 let token = token_id.try_into()?;272 let budget = self273 .recorder274 .weight_calls_budget(<StructureWeight<T>>::find_parent());275276 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved), &budget)277 .map_err(dispatch_to_evm::<T>)?;278 Ok(())279 }280281 /// Not implemented282 fn set_approval_for_all(283 &mut self,284 _caller: caller,285 _operator: address,286 _approved: bool,287 ) -> Result<void> {288 // TODO: Not implemetable289 Err("not implemented".into())290 }291292 /// Not implemented293 fn get_approved(&self, _token_id: uint256) -> Result<address> {294 // TODO: Not implemetable295 Err("not implemented".into())296 }297298 /// Not implemented299 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {300 // TODO: Not implemetable301 Err("not implemented".into())302 }303}304305#[solidity_interface(name = "ERC721Burnable")]306impl<T: Config> NonfungibleHandle<T> {307 #[weight(<SelfWeightOf<T>>::burn_item())]308 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {309 let caller = T::CrossAccountId::from_eth(caller);310 let token = token_id.try_into()?;311312 <Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;313 Ok(())314 }315}316317#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]318impl<T: Config> NonfungibleHandle<T> {319 fn minting_finished(&self) -> Result<bool> {320 Ok(false)321 }322323 /// `token_id` should be obtained with `next_token_id` method,324 /// unlike standard, you can't specify it manually325 #[weight(<SelfWeightOf<T>>::create_item())]326 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {327 let caller = T::CrossAccountId::from_eth(caller);328 let to = T::CrossAccountId::from_eth(to);329 let token_id: u32 = token_id.try_into()?;330 let budget = self331 .recorder332 .weight_calls_budget(<StructureWeight<T>>::find_parent());333334 if <TokensMinted<T>>::get(self.id)335 .checked_add(1)336 .ok_or("item id overflow")?337 != token_id338 {339 return Err("item id should be next".into());340 }341342 <Pallet<T>>::create_item(343 self,344 &caller,345 CreateItemData::<T> {346 properties: BoundedVec::default(),347 owner: to,348 },349 &budget,350 )351 .map_err(dispatch_to_evm::<T>)?;352353 Ok(true)354 }355356 /// `token_id` should be obtained with `next_token_id` method,357 /// unlike standard, you can't specify it manually358 #[solidity(rename_selector = "mintWithTokenURI")]359 #[weight(<SelfWeightOf<T>>::create_item())]360 fn mint_with_token_uri(361 &mut self,362 caller: caller,363 to: address,364 token_id: uint256,365 token_uri: string,366 ) -> Result<bool> {367 let key = token_uri_key();368 let permission = get_token_permission::<T>(self.id, &key)?;369 if !permission.collection_admin {370 return Err("Operation is not allowed".into());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 let mut properties = CollectionPropertiesVec::default();389 properties390 .try_push(Property {391 key,392 value: token_uri393 .into_bytes()394 .try_into()395 .map_err(|_| "token uri is too long")?,396 })397 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;398399 <Pallet<T>>::create_item(400 self,401 &caller,402 CreateItemData::<T> {403 properties,404 owner: to,405 },406 &budget,407 )408 .map_err(dispatch_to_evm::<T>)?;409 Ok(true)410 }411412 /// Not implemented413 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {414 Err("not implementable".into())415 }416}417418fn get_token_permission<T: Config>(419 collection_id: CollectionId,420 key: &PropertyKey,421) -> Result<PropertyPermission> {422 let token_property_permissions = CollectionPropertyPermissions::<T>::try_get(collection_id)423 .map_err(|_| Error::Revert("No permissions for collection".into()))?;424 let a = token_property_permissions425 .get(key)426 .map(|p| p.clone())427 .ok_or_else(|| Error::Revert("No permission".into()))?;428 Ok(a)429}430431fn has_token_permission<T: Config>(collection_id: CollectionId, key: &PropertyKey) -> bool {432 if let Ok(token_property_permissions) =433 CollectionPropertyPermissions::<T>::try_get(collection_id)434 {435 return token_property_permissions.contains_key(key);436 }437438 false439}440441#[solidity_interface(name = "ERC721UniqueExtensions")]442impl<T: Config> NonfungibleHandle<T> {443 #[weight(<SelfWeightOf<T>>::transfer())]444 fn transfer(445 &mut self,446 caller: caller,447 to: address,448 token_id: uint256,449 _value: value,450 ) -> Result<void> {451 let caller = T::CrossAccountId::from_eth(caller);452 let to = T::CrossAccountId::from_eth(to);453 let token = token_id.try_into()?;454 let budget = self455 .recorder456 .weight_calls_budget(<StructureWeight<T>>::find_parent());457458 <Pallet<T>>::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::<T>)?;459 Ok(())460 }461462 #[weight(<SelfWeightOf<T>>::burn_from())]463 fn burn_from(464 &mut self,465 caller: caller,466 from: address,467 token_id: uint256,468 _value: value,469 ) -> Result<void> {470 let caller = T::CrossAccountId::from_eth(caller);471 let from = T::CrossAccountId::from_eth(from);472 let token = token_id.try_into()?;473 let budget = self474 .recorder475 .weight_calls_budget(<StructureWeight<T>>::find_parent());476477 <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)478 .map_err(dispatch_to_evm::<T>)?;479 Ok(())480 }481482 fn next_token_id(&self) -> Result<uint256> {483 self.consume_store_reads(1)?;484 Ok(<TokensMinted<T>>::get(self.id)485 .checked_add(1)486 .ok_or("item id overflow")?487 .into())488 }489490 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]491 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {492 let caller = T::CrossAccountId::from_eth(caller);493 let to = T::CrossAccountId::from_eth(to);494 let mut expected_index = <TokensMinted<T>>::get(self.id)495 .checked_add(1)496 .ok_or("item id overflow")?;497 let budget = self498 .recorder499 .weight_calls_budget(<StructureWeight<T>>::find_parent());500501 let total_tokens = token_ids.len();502 for id in token_ids.into_iter() {503 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;504 if id != expected_index {505 return Err("item id should be next".into());506 }507 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;508 }509 let data = (0..total_tokens)510 .map(|_| CreateItemData::<T> {511 properties: BoundedVec::default(),512 owner: to.clone(),513 })514 .collect();515516 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)517 .map_err(dispatch_to_evm::<T>)?;518 Ok(true)519 }520521 #[solidity(rename_selector = "mintBulkWithTokenURI")]522 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]523 fn mint_bulk_with_token_uri(524 &mut self,525 caller: caller,526 to: address,527 tokens: Vec<(uint256, string)>,528 ) -> Result<bool> {529 let key = token_uri_key();530 let caller = T::CrossAccountId::from_eth(caller);531 let to = T::CrossAccountId::from_eth(to);532 let mut expected_index = <TokensMinted<T>>::get(self.id)533 .checked_add(1)534 .ok_or("item id overflow")?;535 let budget = self536 .recorder537 .weight_calls_budget(<StructureWeight<T>>::find_parent());538539 let mut data = Vec::with_capacity(tokens.len());540 for (id, token_uri) in tokens {541 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;542 if id != expected_index {543 return Err("item id should be next".into());544 }545 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;546547 let mut properties = CollectionPropertiesVec::default();548 properties549 .try_push(Property {550 key: key.clone(),551 value: token_uri552 .into_bytes()553 .try_into()554 .map_err(|_| "token uri is too long")?,555 })556 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;557558 data.push(CreateItemData::<T> {559 properties,560 owner: to.clone(),561 });562 }563564 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)565 .map_err(dispatch_to_evm::<T>)?;566 Ok(true)567 }568}569570#[solidity_interface(571 name = "UniqueNFT",572 is(573 ERC721,574 ERC721Metadata,575 ERC721Enumerable,576 ERC721UniqueExtensions,577 ERC721Mintable,578 ERC721Burnable,579 via("CollectionHandle<T>", common_mut, Collection),580 TokenProperties,581 )582)]583impl<T: Config> NonfungibleHandle<T> {}584585// Not a tests, but code generators586generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);587generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);588589impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {590 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");591592 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {593 call::<T, UniqueNFTCall<T>, _, _>(handle, self)594 }595}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,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 <Pallet<T>>::set_token_property(self, &caller, TokenId(token_id), Property { key, value })86 .map_err(dispatch_to_evm::<T>)87 }8889 fn delete_property(&mut self, token_id: uint256, caller: caller, key: string) -> Result<()> {90 let caller = T::CrossAccountId::from_eth(caller);91 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;92 let key = <Vec<u8>>::from(key)93 .try_into()94 .map_err(|_| "key too long")?;9596 <Pallet<T>>::delete_token_property(self, &caller, TokenId(token_id), key)97 .map_err(dispatch_to_evm::<T>)98 }99100 /// Throws error if key not found101 fn property(&self, token_id: uint256, key: string) -> Result<bytes> {102 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;103 let key = <Vec<u8>>::from(key)104 .try_into()105 .map_err(|_| "key too long")?;106107 let props = <TokenProperties<T>>::get((self.id, token_id));108 let prop = props.get(&key).ok_or("key not found")?;109110 Ok(prop.to_vec())111 }112}113114#[derive(ToLog)]115pub enum ERC721Events {116 Transfer {117 #[indexed]118 from: address,119 #[indexed]120 to: address,121 #[indexed]122 token_id: uint256,123 },124 Approval {125 #[indexed]126 owner: address,127 #[indexed]128 approved: address,129 #[indexed]130 token_id: uint256,131 },132 #[allow(dead_code)]133 ApprovalForAll {134 #[indexed]135 owner: address,136 #[indexed]137 operator: address,138 approved: bool,139 },140}141142#[derive(ToLog)]143pub enum ERC721MintableEvents {144 #[allow(dead_code)]145 MintingFinished {},146}147148#[solidity_interface(name = "ERC721Metadata")]149impl<T: Config> NonfungibleHandle<T> {150 fn name(&self) -> Result<string> {151 Ok(decode_utf16(self.name.iter().copied())152 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))153 .collect::<string>())154 }155156 fn symbol(&self) -> Result<string> {157 Ok(string::from_utf8_lossy(&self.token_prefix).into())158 }159160 /// Returns token's const_metadata161 #[solidity(rename_selector = "tokenURI")]162 fn token_uri(&self, token_id: uint256) -> Result<string> {163 let key = token_uri_key();164 if !has_token_permission::<T>(self.id, &key) {165 return Err("No tokenURI permission".into());166 }167168 self.consume_store_reads(1)?;169 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;170171 let properties = <TokenProperties<T>>::try_get((self.id, token_id))172 .map_err(|_| Error::Revert("Token properties not found".into()))?;173 if let Some(property) = properties.get(&key) {174 return Ok(string::from_utf8_lossy(property).into());175 }176177 Err("Property tokenURI not found".into())178 }179}180181#[solidity_interface(name = "ERC721Enumerable")]182impl<T: Config> NonfungibleHandle<T> {183 fn token_by_index(&self, index: uint256) -> Result<uint256> {184 Ok(index)185 }186187 /// Not implemented188 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {189 // TODO: Not implemetable190 Err("not implemented".into())191 }192193 fn total_supply(&self) -> Result<uint256> {194 self.consume_store_reads(1)?;195 Ok(<Pallet<T>>::total_supply(self).into())196 }197}198199#[solidity_interface(name = "ERC721", events(ERC721Events))]200impl<T: Config> NonfungibleHandle<T> {201 fn balance_of(&self, owner: address) -> Result<uint256> {202 self.consume_store_reads(1)?;203 let owner = T::CrossAccountId::from_eth(owner);204 let balance = <AccountBalance<T>>::get((self.id, owner));205 Ok(balance.into())206 }207 fn owner_of(&self, token_id: uint256) -> Result<address> {208 self.consume_store_reads(1)?;209 let token: TokenId = token_id.try_into()?;210 Ok(*<TokenData<T>>::get((self.id, token))211 .ok_or("token not found")?212 .owner213 .as_eth())214 }215 /// Not implemented216 fn safe_transfer_from_with_data(217 &mut self,218 _from: address,219 _to: address,220 _token_id: uint256,221 _data: bytes,222 _value: value,223 ) -> Result<void> {224 // TODO: Not implemetable225 Err("not implemented".into())226 }227 /// Not implemented228 fn safe_transfer_from(229 &mut self,230 _from: address,231 _to: address,232 _token_id: uint256,233 _value: value,234 ) -> Result<void> {235 // TODO: Not implemetable236 Err("not implemented".into())237 }238239 #[weight(<SelfWeightOf<T>>::transfer_from())]240 fn transfer_from(241 &mut self,242 caller: caller,243 from: address,244 to: address,245 token_id: uint256,246 _value: value,247 ) -> Result<void> {248 let caller = T::CrossAccountId::from_eth(caller);249 let from = T::CrossAccountId::from_eth(from);250 let to = T::CrossAccountId::from_eth(to);251 let token = token_id.try_into()?;252 let budget = self253 .recorder254 .weight_calls_budget(<StructureWeight<T>>::find_parent());255256 <Pallet<T>>::transfer_from(self, &caller, &from, &to, token, &budget)257 .map_err(dispatch_to_evm::<T>)?;258 Ok(())259 }260261 #[weight(<SelfWeightOf<T>>::approve())]262 fn approve(263 &mut self,264 caller: caller,265 approved: address,266 token_id: uint256,267 _value: value,268 ) -> Result<void> {269 let caller = T::CrossAccountId::from_eth(caller);270 let approved = T::CrossAccountId::from_eth(approved);271 let token = token_id.try_into()?;272273 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))274 .map_err(dispatch_to_evm::<T>)?;275 Ok(())276 }277278 /// Not implemented279 fn set_approval_for_all(280 &mut self,281 _caller: caller,282 _operator: address,283 _approved: bool,284 ) -> Result<void> {285 // TODO: Not implemetable286 Err("not implemented".into())287 }288289 /// Not implemented290 fn get_approved(&self, _token_id: uint256) -> Result<address> {291 // TODO: Not implemetable292 Err("not implemented".into())293 }294295 /// Not implemented296 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {297 // TODO: Not implemetable298 Err("not implemented".into())299 }300}301302#[solidity_interface(name = "ERC721Burnable")]303impl<T: Config> NonfungibleHandle<T> {304 #[weight(<SelfWeightOf<T>>::burn_item())]305 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {306 let caller = T::CrossAccountId::from_eth(caller);307 let token = token_id.try_into()?;308309 <Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;310 Ok(())311 }312}313314#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]315impl<T: Config> NonfungibleHandle<T> {316 fn minting_finished(&self) -> Result<bool> {317 Ok(false)318 }319320 /// `token_id` should be obtained with `next_token_id` method,321 /// unlike standard, you can't specify it manually322 #[weight(<SelfWeightOf<T>>::create_item())]323 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {324 let caller = T::CrossAccountId::from_eth(caller);325 let to = T::CrossAccountId::from_eth(to);326 let token_id: u32 = token_id.try_into()?;327 let budget = self328 .recorder329 .weight_calls_budget(<StructureWeight<T>>::find_parent());330331 if <TokensMinted<T>>::get(self.id)332 .checked_add(1)333 .ok_or("item id overflow")?334 != token_id335 {336 return Err("item id should be next".into());337 }338339 <Pallet<T>>::create_item(340 self,341 &caller,342 CreateItemData::<T> {343 properties: BoundedVec::default(),344 owner: to,345 },346 &budget,347 )348 .map_err(dispatch_to_evm::<T>)?;349350 Ok(true)351 }352353 /// `token_id` should be obtained with `next_token_id` method,354 /// unlike standard, you can't specify it manually355 #[solidity(rename_selector = "mintWithTokenURI")]356 #[weight(<SelfWeightOf<T>>::create_item())]357 fn mint_with_token_uri(358 &mut self,359 caller: caller,360 to: address,361 token_id: uint256,362 token_uri: string,363 ) -> Result<bool> {364 let key = token_uri_key();365 let permission = get_token_permission::<T>(self.id, &key)?;366 if !permission.collection_admin {367 return Err("Operation is not allowed".into());368 }369370 let caller = T::CrossAccountId::from_eth(caller);371 let to = T::CrossAccountId::from_eth(to);372 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;373 let budget = self374 .recorder375 .weight_calls_budget(<StructureWeight<T>>::find_parent());376377 if <TokensMinted<T>>::get(self.id)378 .checked_add(1)379 .ok_or("item id overflow")?380 != token_id381 {382 return Err("item id should be next".into());383 }384385 let mut properties = CollectionPropertiesVec::default();386 properties387 .try_push(Property {388 key,389 value: token_uri390 .into_bytes()391 .try_into()392 .map_err(|_| "token uri is too long")?,393 })394 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;395396 <Pallet<T>>::create_item(397 self,398 &caller,399 CreateItemData::<T> {400 properties,401 owner: to,402 },403 &budget,404 )405 .map_err(dispatch_to_evm::<T>)?;406 Ok(true)407 }408409 /// Not implemented410 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {411 Err("not implementable".into())412 }413}414415fn get_token_permission<T: Config>(416 collection_id: CollectionId,417 key: &PropertyKey,418) -> Result<PropertyPermission> {419 let token_property_permissions = CollectionPropertyPermissions::<T>::try_get(collection_id)420 .map_err(|_| Error::Revert("No permissions for collection".into()))?;421 let a = token_property_permissions422 .get(key)423 .map(|p| p.clone())424 .ok_or_else(|| Error::Revert("No permission".into()))?;425 Ok(a)426}427428fn has_token_permission<T: Config>(collection_id: CollectionId, key: &PropertyKey) -> bool {429 if let Ok(token_property_permissions) =430 CollectionPropertyPermissions::<T>::try_get(collection_id)431 {432 return token_property_permissions.contains_key(key);433 }434435 false436}437438#[solidity_interface(name = "ERC721UniqueExtensions")]439impl<T: Config> NonfungibleHandle<T> {440 #[weight(<SelfWeightOf<T>>::transfer())]441 fn transfer(442 &mut self,443 caller: caller,444 to: address,445 token_id: uint256,446 _value: value,447 ) -> Result<void> {448 let caller = T::CrossAccountId::from_eth(caller);449 let to = T::CrossAccountId::from_eth(to);450 let token = token_id.try_into()?;451 let budget = self452 .recorder453 .weight_calls_budget(<StructureWeight<T>>::find_parent());454455 <Pallet<T>>::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::<T>)?;456 Ok(())457 }458459 #[weight(<SelfWeightOf<T>>::burn_from())]460 fn burn_from(461 &mut self,462 caller: caller,463 from: address,464 token_id: uint256,465 _value: value,466 ) -> Result<void> {467 let caller = T::CrossAccountId::from_eth(caller);468 let from = T::CrossAccountId::from_eth(from);469 let token = token_id.try_into()?;470 let budget = self471 .recorder472 .weight_calls_budget(<StructureWeight<T>>::find_parent());473474 <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)475 .map_err(dispatch_to_evm::<T>)?;476 Ok(())477 }478479 fn next_token_id(&self) -> Result<uint256> {480 self.consume_store_reads(1)?;481 Ok(<TokensMinted<T>>::get(self.id)482 .checked_add(1)483 .ok_or("item id overflow")?484 .into())485 }486487 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]488 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {489 let caller = T::CrossAccountId::from_eth(caller);490 let to = T::CrossAccountId::from_eth(to);491 let mut expected_index = <TokensMinted<T>>::get(self.id)492 .checked_add(1)493 .ok_or("item id overflow")?;494 let budget = self495 .recorder496 .weight_calls_budget(<StructureWeight<T>>::find_parent());497498 let total_tokens = token_ids.len();499 for id in token_ids.into_iter() {500 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;501 if id != expected_index {502 return Err("item id should be next".into());503 }504 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;505 }506 let data = (0..total_tokens)507 .map(|_| CreateItemData::<T> {508 properties: BoundedVec::default(),509 owner: to.clone(),510 })511 .collect();512513 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)514 .map_err(dispatch_to_evm::<T>)?;515 Ok(true)516 }517518 #[solidity(rename_selector = "mintBulkWithTokenURI")]519 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]520 fn mint_bulk_with_token_uri(521 &mut self,522 caller: caller,523 to: address,524 tokens: Vec<(uint256, string)>,525 ) -> Result<bool> {526 let key = token_uri_key();527 let caller = T::CrossAccountId::from_eth(caller);528 let to = T::CrossAccountId::from_eth(to);529 let mut expected_index = <TokensMinted<T>>::get(self.id)530 .checked_add(1)531 .ok_or("item id overflow")?;532 let budget = self533 .recorder534 .weight_calls_budget(<StructureWeight<T>>::find_parent());535536 let mut data = Vec::with_capacity(tokens.len());537 for (id, token_uri) in tokens {538 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;539 if id != expected_index {540 return Err("item id should be next".into());541 }542 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;543544 let mut properties = CollectionPropertiesVec::default();545 properties546 .try_push(Property {547 key: key.clone(),548 value: token_uri549 .into_bytes()550 .try_into()551 .map_err(|_| "token uri is too long")?,552 })553 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;554555 data.push(CreateItemData::<T> {556 properties,557 owner: to.clone(),558 });559 }560561 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)562 .map_err(dispatch_to_evm::<T>)?;563 Ok(true)564 }565}566567#[solidity_interface(568 name = "UniqueNFT",569 is(570 ERC721,571 ERC721Metadata,572 ERC721Enumerable,573 ERC721UniqueExtensions,574 ERC721Mintable,575 ERC721Burnable,576 via("CollectionHandle<T>", common_mut, Collection),577 TokenProperties,578 )579)]580impl<T: Config> NonfungibleHandle<T> {}581582// Not a tests, but code generators583generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);584generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);585586impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {587 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");588589 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {590 call::<T, UniqueNFTCall<T>, _, _>(handle, self)591 }592}pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -878,7 +878,6 @@
sender: &T::CrossAccountId,
token: TokenId,
spender: Option<&T::CrossAccountId>,
- nesting_budget: &dyn Budget,
) -> DispatchResult {
if collection.permissions.access() == AccessMode::AllowList {
collection.check_allowlist(sender)?;
@@ -890,16 +889,10 @@
if let Some(spender) = spender {
<PalletCommon<T>>::ensure_correct_receiver(spender)?;
}
-
- let is_owned = <PalletStructure<T>>::check_indirectly_owned(
- sender.clone(),
- collection.id,
- token,
- None,
- nesting_budget
- )?;
- if !is_owned {
+ let token_data =
+ <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;
+ if &token_data.owner != sender {
ensure!(
collection.ignores_owned_amount(sender),
<CommonError<T>>::CantApproveMoreThanOwned
@@ -926,9 +919,6 @@
// `from`, `to` checked in [`transfer`]
collection.check_allowlist(spender)?;
}
- if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
- return Ok(());
- }
if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {
// TODO: should collection owner be allowed to perform this transfer?
ensure!(
@@ -943,6 +933,9 @@
);
return Ok(());
}
+ if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
+ return Ok(());
+ }
ensure!(
collection.ignores_allowance(spender),
<CommonError<T>>::ApprovedValueTooLow
pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -414,13 +414,14 @@
let is_approval_required = cross_sender != spender;
if is_approval_required {
- <PalletNft<T>>::set_allowance(
- &collection,
- &cross_sender,
- nft_id,
- Some(&spender),
- &budget
- ).map_err(Self::map_common_err_to_proxy)?;
+ // FIXME
+ // <PalletNft<T>>::set_allowance(
+ // &collection,
+ // &cross_sender,
+ // nft_id,
+ // Some(&spender),
+ // &budget
+ // ).map_err(Self::map_common_err_to_proxy)?;
return Ok(());
}
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -254,7 +254,6 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
- _nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo {
with_weight(
<Pallet<T>>::set_allowance(self, &sender, &spender, token, amount),
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -815,9 +815,8 @@
#[transactional]
pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
- let budget = budget::Value::new(NESTING_BUDGET);
- dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount, &budget))
+ dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))
}
/// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.