difftreelog
fix token uri == const_data
in: master
2 files changed
pallets/nft/src/eth/erc.rsdiffbeforeafterboth1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};2use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};3use nft_data_structs::{CreateItemData, CreateNftData};4use core::convert::TryInto;5use alloc::format;6use crate::{7 Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList,8 ItemListIndex,9};10use frame_support::storage::{StorageMap, StorageDoubleMap};11use pallet_evm::AddressMapping;12use super::account::CrossAccountId;13use sp_std::{vec, vec::Vec};1415#[solidity_interface(name = "ERC165")]16impl<T: Config> CollectionHandle<T> {17 fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {18 Ok(match self.mode {19 CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),20 CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),21 _ => false,22 })23 }24}2526#[solidity_interface(name = "InlineNameSymbol")]27impl<T: Config> CollectionHandle<T> {28 fn name(&self) -> Result<string> {29 Ok(decode_utf16(self.name.iter().copied())30 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))31 .collect::<string>())32 }3334 fn symbol(&self) -> Result<string> {35 Ok(string::from_utf8_lossy(&self.token_prefix).into())36 }37}3839#[solidity_interface(name = "InlineTotalSupply")]40impl<T: Config> CollectionHandle<T> {41 fn total_supply(&self) -> Result<uint256> {42 // TODO: we do not track total amount of all tokens43 Ok(0.into())44 }45}4647#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]48impl<T: Config> CollectionHandle<T> {49 fn token_uri(&self, token_id: uint256) -> Result<string> {50 // TODO: We should standartize url prefix, maybe via offchain schema?51 Ok(format!("unique.network/{}/{}", self.id, token_id))52 }53}5455#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))]56impl<T: Config> CollectionHandle<T> {57 fn token_by_index(&self, index: uint256) -> Result<uint256> {58 Ok(index)59 }6061 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {62 // TODO: Not implemetable63 Err("not implemented".into())64 }65}6667#[derive(ToLog)]68pub enum ERC721Events {69 Transfer {70 #[indexed]71 from: address,72 #[indexed]73 to: address,74 #[indexed]75 token_id: uint256,76 },77 Approval {78 #[indexed]79 owner: address,80 #[indexed]81 approved: address,82 #[indexed]83 token_id: uint256,84 },85 #[allow(dead_code)]86 ApprovalForAll {87 #[indexed]88 owner: address,89 #[indexed]90 operator: address,91 approved: bool,92 },93}9495#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]96impl<T: Config> CollectionHandle<T> {97 #[solidity(rename_selector = "balanceOf")]98 fn balance_of_nft(&self, owner: address) -> Result<uint256> {99 let owner = T::EvmAddressMapping::into_account_id(owner);100 let balance = <Balance<T>>::get(self.id, owner);101 Ok(balance.into())102 }103 fn owner_of(&self, token_id: uint256) -> Result<address> {104 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;105 let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;106 Ok(*token.owner.as_eth())107 }108 fn safe_transfer_from_with_data(109 &mut self,110 _from: address,111 _to: address,112 _token_id: uint256,113 _data: bytes,114 _value: value,115 ) -> Result<void> {116 // TODO: Not implemetable117 Err("not implemented".into())118 }119 fn safe_transfer_from(120 &mut self,121 _from: address,122 _to: address,123 _token_id: uint256,124 _value: value,125 ) -> Result<void> {126 // TODO: Not implemetable127 Err("not implemented".into())128 }129130 fn transfer_from(131 &mut self,132 caller: caller,133 from: address,134 to: address,135 token_id: uint256,136 _value: value,137 ) -> Result<void> {138 let caller = T::CrossAccountId::from_eth(caller);139 let from = T::CrossAccountId::from_eth(from);140 let to = T::CrossAccountId::from_eth(to);141 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;142143 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)144 .map_err(|_| "transferFrom error")?;145 Ok(())146 }147148 fn approve(149 &mut self,150 caller: caller,151 approved: address,152 token_id: uint256,153 _value: value,154 ) -> Result<void> {155 let caller = T::CrossAccountId::from_eth(caller);156 let approved = T::CrossAccountId::from_eth(approved);157 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;158159 <Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)160 .map_err(|_| "approve internal")?;161 Ok(())162 }163164 fn set_approval_for_all(165 &mut self,166 _caller: caller,167 _operator: address,168 _approved: bool,169 ) -> Result<void> {170 // TODO: Not implemetable171 Err("not implemented".into())172 }173174 fn get_approved(&self, _token_id: uint256) -> Result<address> {175 // TODO: Not implemetable176 Err("not implemented".into())177 }178179 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {180 // TODO: Not implemetable181 Err("not implemented".into())182 }183}184185#[solidity_interface(name = "ERC721Burnable")]186impl<T: Config> CollectionHandle<T> {187 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {188 let caller = T::CrossAccountId::from_eth(caller);189 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;190191 <Module<T>>::burn_item_internal(&caller, &self, token_id, 1).map_err(|_| "burn error")?;192 Ok(())193 }194}195196#[derive(ToLog)]197pub enum ERC721MintableEvents {198 #[allow(dead_code)]199 MintingFinished {},200}201202#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]203impl<T: Config> CollectionHandle<T> {204 fn minting_finished(&self) -> Result<bool> {205 Ok(false)206 }207208 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {209 let caller = T::CrossAccountId::from_eth(caller);210 let to = T::CrossAccountId::from_eth(to);211 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;212 if <ItemListIndex>::get(self.id)213 .checked_add(1)214 .ok_or("item id overflow")?215 != token_id216 {217 return Err("item id should be next".into());218 }219220 <Module<T>>::create_item_internal(221 &caller,222 &self,223 &to,224 CreateItemData::NFT(CreateNftData {225 const_data: vec![],226 variable_data: vec![],227 }),228 )229 .map_err(|_| "mint error")?;230 Ok(true)231 }232233 #[solidity(rename_selector = "mintWithTokenURI")]234 fn mint_with_token_uri(235 &mut self,236 caller: caller,237 to: address,238 token_id: uint256,239 token_uri: string,240 ) -> Result<bool> {241 let caller = T::CrossAccountId::from_eth(caller);242 let to = T::CrossAccountId::from_eth(to);243 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;244 if <ItemListIndex>::get(self.id)245 .checked_add(1)246 .ok_or("item id overflow")?247 != token_id248 {249 return Err("item id should be next".into());250 }251252 <Module<T>>::create_item_internal(253 &caller,254 &self,255 &to,256 CreateItemData::NFT(CreateNftData {257 const_data: token_uri.into(),258 variable_data: vec![],259 }),260 )261 .map_err(|_| "mint error")?;262 Ok(true)263 }264265 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {266 Err("not implementable".into())267 }268}269270#[solidity_interface(name = "ERC721UniqueExtensions")]271impl<T: Config> CollectionHandle<T> {272 #[solidity(rename_selector = "transfer")]273 fn transfer_nft(274 &mut self,275 caller: caller,276 to: address,277 token_id: uint256,278 _value: value,279 ) -> Result<void> {280 let caller = T::CrossAccountId::from_eth(caller);281 let to = T::CrossAccountId::from_eth(to);282 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;283284 <Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)285 .map_err(|_| "transfer error")?;286 Ok(())287 }288289 fn next_token_id(&self) -> Result<uint256> {290 Ok(ItemListIndex::get(self.id)291 .checked_add(1)292 .ok_or("item id overflow")?293 .into())294 }295}296297#[solidity_interface(298 name = "UniqueNFT",299 is(300 ERC165,301 ERC721,302 ERC721Metadata,303 ERC721Enumerable,304 ERC721UniqueExtensions,305 ERC721Mintable,306 ERC721Burnable,307 )308)]309impl<T: Config> CollectionHandle<T> {}310311#[derive(ToLog)]312pub enum ERC20Events {313 Transfer {314 #[indexed]315 from: address,316 #[indexed]317 to: address,318 value: uint256,319 },320 Approval {321 #[indexed]322 owner: address,323 #[indexed]324 spender: address,325 value: uint256,326 },327}328329#[solidity_interface(330 name = "ERC20",331 inline_is(InlineNameSymbol, InlineTotalSupply),332 events(ERC20Events)333)]334impl<T: Config> CollectionHandle<T> {335 fn decimals(&self) -> Result<uint8> {336 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {337 *decimals338 } else {339 unreachable!()340 })341 }342 fn balance_of(&self, owner: address) -> Result<uint256> {343 let owner = T::EvmAddressMapping::into_account_id(owner);344 let balance = <Balance<T>>::get(self.id, owner);345 Ok(balance.into())346 }347 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {348 let caller = T::CrossAccountId::from_eth(caller);349 let to = T::CrossAccountId::from_eth(to);350 let amount = amount.try_into().map_err(|_| "amount overflow")?;351352 <Module<T>>::transfer_internal(&caller, &to, self, 1, amount)353 .map_err(|_| "transfer error")?;354 Ok(true)355 }356 #[solidity(rename_selector = "transferFrom")]357 fn transfer_from_fungible(358 &mut self,359 caller: caller,360 from: address,361 to: address,362 amount: uint256,363 ) -> Result<bool> {364 let caller = T::CrossAccountId::from_eth(caller);365 let from = T::CrossAccountId::from_eth(from);366 let to = T::CrossAccountId::from_eth(to);367 let amount = amount.try_into().map_err(|_| "amount overflow")?;368369 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)370 .map_err(|_| "transferFrom error")?;371 Ok(true)372 }373 #[solidity(rename_selector = "approve")]374 fn approve_fungible(375 &mut self,376 caller: caller,377 spender: address,378 amount: uint256,379 ) -> Result<bool> {380 let caller = T::CrossAccountId::from_eth(caller);381 let spender = T::CrossAccountId::from_eth(spender);382 let amount = amount.try_into().map_err(|_| "amount overflow")?;383384 <Module<T>>::approve_internal(&caller, &spender, self, 1, amount)385 .map_err(|_| "approve internal")?;386 Ok(true)387 }388 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {389 let owner = T::CrossAccountId::from_eth(owner);390 let spender = T::CrossAccountId::from_eth(spender);391392 Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())393 }394}395396#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]397impl<T: Config> CollectionHandle<T> {}398399macro_rules! generate_code {400 ($name:ident, $decl:ident, $is_impl:literal) => {401 #[test]402 #[ignore]403 fn $name() {404 use sp_std::collections::btree_set::BTreeSet;405 let mut out = BTreeSet::new();406 $decl::generate_solidity_interface(&mut out, $is_impl);407 println!("=== SNIP START ===");408 println!("// SPDX-License-Identifier: OTHER");409 println!("// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::{} --exact --nocapture --ignored`", stringify!(name));410 println!();411 println!("pragma solidity >=0.8.0 <0.9.0;");412 println!();413 for b in out {414 println!("{}", b);415 }416 println!("=== SNIP END ===");417 }418 };419}420421// Not a tests, but code generators422generate_code!(nft_impl, UniqueNFTCall, true);423generate_code!(nft_iface, UniqueNFTCall, false);424425generate_code!(fungible_impl, UniqueFungibleCall, true);426generate_code!(fungible_iface, UniqueFungibleCall, false);1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};2use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};3use nft_data_structs::{CreateItemData, CreateNftData};4use core::convert::TryInto;5use crate::{6 Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList,7 ItemListIndex,8};9use frame_support::storage::{StorageMap, StorageDoubleMap};10use pallet_evm::AddressMapping;11use super::account::CrossAccountId;12use sp_std::{vec, vec::Vec};1314#[solidity_interface(name = "ERC165")]15impl<T: Config> CollectionHandle<T> {16 fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {17 Ok(match self.mode {18 CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),19 CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),20 _ => false,21 })22 }23}2425#[solidity_interface(name = "InlineNameSymbol")]26impl<T: Config> CollectionHandle<T> {27 fn name(&self) -> Result<string> {28 Ok(decode_utf16(self.name.iter().copied())29 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))30 .collect::<string>())31 }3233 fn symbol(&self) -> Result<string> {34 Ok(string::from_utf8_lossy(&self.token_prefix).into())35 }36}3738#[solidity_interface(name = "InlineTotalSupply")]39impl<T: Config> CollectionHandle<T> {40 fn total_supply(&self) -> Result<uint256> {41 // TODO: we do not track total amount of all tokens42 Ok(0.into())43 }44}4546#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]47impl<T: Config> CollectionHandle<T> {48 #[solidity(rename_selector = "tokenURI")]49 fn token_uri(&self, token_id: uint256) -> Result<string> {50 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;51 Ok(string::from_utf8_lossy(52 &<NftItemList<T>>::get(self.id, token_id)53 .ok_or("token not found")?54 .const_data,55 )56 .into())57 }58}5960#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))]61impl<T: Config> CollectionHandle<T> {62 fn token_by_index(&self, index: uint256) -> Result<uint256> {63 Ok(index)64 }6566 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {67 // TODO: Not implemetable68 Err("not implemented".into())69 }70}7172#[derive(ToLog)]73pub enum ERC721Events {74 Transfer {75 #[indexed]76 from: address,77 #[indexed]78 to: address,79 #[indexed]80 token_id: uint256,81 },82 Approval {83 #[indexed]84 owner: address,85 #[indexed]86 approved: address,87 #[indexed]88 token_id: uint256,89 },90 #[allow(dead_code)]91 ApprovalForAll {92 #[indexed]93 owner: address,94 #[indexed]95 operator: address,96 approved: bool,97 },98}99100#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]101impl<T: Config> CollectionHandle<T> {102 #[solidity(rename_selector = "balanceOf")]103 fn balance_of_nft(&self, owner: address) -> Result<uint256> {104 let owner = T::EvmAddressMapping::into_account_id(owner);105 let balance = <Balance<T>>::get(self.id, owner);106 Ok(balance.into())107 }108 fn owner_of(&self, token_id: uint256) -> Result<address> {109 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;110 let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;111 Ok(*token.owner.as_eth())112 }113 fn safe_transfer_from_with_data(114 &mut self,115 _from: address,116 _to: address,117 _token_id: uint256,118 _data: bytes,119 _value: value,120 ) -> Result<void> {121 // TODO: Not implemetable122 Err("not implemented".into())123 }124 fn safe_transfer_from(125 &mut self,126 _from: address,127 _to: address,128 _token_id: uint256,129 _value: value,130 ) -> Result<void> {131 // TODO: Not implemetable132 Err("not implemented".into())133 }134135 fn transfer_from(136 &mut self,137 caller: caller,138 from: address,139 to: address,140 token_id: uint256,141 _value: value,142 ) -> Result<void> {143 let caller = T::CrossAccountId::from_eth(caller);144 let from = T::CrossAccountId::from_eth(from);145 let to = T::CrossAccountId::from_eth(to);146 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;147148 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)149 .map_err(|_| "transferFrom error")?;150 Ok(())151 }152153 fn approve(154 &mut self,155 caller: caller,156 approved: address,157 token_id: uint256,158 _value: value,159 ) -> Result<void> {160 let caller = T::CrossAccountId::from_eth(caller);161 let approved = T::CrossAccountId::from_eth(approved);162 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;163164 <Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)165 .map_err(|_| "approve internal")?;166 Ok(())167 }168169 fn set_approval_for_all(170 &mut self,171 _caller: caller,172 _operator: address,173 _approved: bool,174 ) -> Result<void> {175 // TODO: Not implemetable176 Err("not implemented".into())177 }178179 fn get_approved(&self, _token_id: uint256) -> Result<address> {180 // TODO: Not implemetable181 Err("not implemented".into())182 }183184 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {185 // TODO: Not implemetable186 Err("not implemented".into())187 }188}189190#[solidity_interface(name = "ERC721Burnable")]191impl<T: Config> CollectionHandle<T> {192 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {193 let caller = T::CrossAccountId::from_eth(caller);194 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;195196 <Module<T>>::burn_item_internal(&caller, &self, token_id, 1).map_err(|_| "burn error")?;197 Ok(())198 }199}200201#[derive(ToLog)]202pub enum ERC721MintableEvents {203 #[allow(dead_code)]204 MintingFinished {},205}206207#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]208impl<T: Config> CollectionHandle<T> {209 fn minting_finished(&self) -> Result<bool> {210 Ok(false)211 }212213 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {214 let caller = T::CrossAccountId::from_eth(caller);215 let to = T::CrossAccountId::from_eth(to);216 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;217 if <ItemListIndex>::get(self.id)218 .checked_add(1)219 .ok_or("item id overflow")?220 != token_id221 {222 return Err("item id should be next".into());223 }224225 <Module<T>>::create_item_internal(226 &caller,227 &self,228 &to,229 CreateItemData::NFT(CreateNftData {230 const_data: vec![],231 variable_data: vec![],232 }),233 )234 .map_err(|_| "mint error")?;235 Ok(true)236 }237238 #[solidity(rename_selector = "mintWithTokenURI")]239 fn mint_with_token_uri(240 &mut self,241 caller: caller,242 to: address,243 token_id: uint256,244 token_uri: string,245 ) -> Result<bool> {246 let caller = T::CrossAccountId::from_eth(caller);247 let to = T::CrossAccountId::from_eth(to);248 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;249 if <ItemListIndex>::get(self.id)250 .checked_add(1)251 .ok_or("item id overflow")?252 != token_id253 {254 return Err("item id should be next".into());255 }256257 <Module<T>>::create_item_internal(258 &caller,259 &self,260 &to,261 CreateItemData::NFT(CreateNftData {262 const_data: token_uri.into(),263 variable_data: vec![],264 }),265 )266 .map_err(|_| "mint error")?;267 Ok(true)268 }269270 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {271 Err("not implementable".into())272 }273}274275#[solidity_interface(name = "ERC721UniqueExtensions")]276impl<T: Config> CollectionHandle<T> {277 #[solidity(rename_selector = "transfer")]278 fn transfer_nft(279 &mut self,280 caller: caller,281 to: address,282 token_id: uint256,283 _value: value,284 ) -> Result<void> {285 let caller = T::CrossAccountId::from_eth(caller);286 let to = T::CrossAccountId::from_eth(to);287 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;288289 <Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)290 .map_err(|_| "transfer error")?;291 Ok(())292 }293294 fn next_token_id(&self) -> Result<uint256> {295 Ok(ItemListIndex::get(self.id)296 .checked_add(1)297 .ok_or("item id overflow")?298 .into())299 }300}301302#[solidity_interface(303 name = "UniqueNFT",304 is(305 ERC165,306 ERC721,307 ERC721Metadata,308 ERC721Enumerable,309 ERC721UniqueExtensions,310 ERC721Mintable,311 ERC721Burnable,312 )313)]314impl<T: Config> CollectionHandle<T> {}315316#[derive(ToLog)]317pub enum ERC20Events {318 Transfer {319 #[indexed]320 from: address,321 #[indexed]322 to: address,323 value: uint256,324 },325 Approval {326 #[indexed]327 owner: address,328 #[indexed]329 spender: address,330 value: uint256,331 },332}333334#[solidity_interface(335 name = "ERC20",336 inline_is(InlineNameSymbol, InlineTotalSupply),337 events(ERC20Events)338)]339impl<T: Config> CollectionHandle<T> {340 fn decimals(&self) -> Result<uint8> {341 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {342 *decimals343 } else {344 unreachable!()345 })346 }347 fn balance_of(&self, owner: address) -> Result<uint256> {348 let owner = T::EvmAddressMapping::into_account_id(owner);349 let balance = <Balance<T>>::get(self.id, owner);350 Ok(balance.into())351 }352 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {353 let caller = T::CrossAccountId::from_eth(caller);354 let to = T::CrossAccountId::from_eth(to);355 let amount = amount.try_into().map_err(|_| "amount overflow")?;356357 <Module<T>>::transfer_internal(&caller, &to, self, 1, amount)358 .map_err(|_| "transfer error")?;359 Ok(true)360 }361 #[solidity(rename_selector = "transferFrom")]362 fn transfer_from_fungible(363 &mut self,364 caller: caller,365 from: address,366 to: address,367 amount: uint256,368 ) -> Result<bool> {369 let caller = T::CrossAccountId::from_eth(caller);370 let from = T::CrossAccountId::from_eth(from);371 let to = T::CrossAccountId::from_eth(to);372 let amount = amount.try_into().map_err(|_| "amount overflow")?;373374 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)375 .map_err(|_| "transferFrom error")?;376 Ok(true)377 }378 #[solidity(rename_selector = "approve")]379 fn approve_fungible(380 &mut self,381 caller: caller,382 spender: address,383 amount: uint256,384 ) -> Result<bool> {385 let caller = T::CrossAccountId::from_eth(caller);386 let spender = T::CrossAccountId::from_eth(spender);387 let amount = amount.try_into().map_err(|_| "amount overflow")?;388389 <Module<T>>::approve_internal(&caller, &spender, self, 1, amount)390 .map_err(|_| "approve internal")?;391 Ok(true)392 }393 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {394 let owner = T::CrossAccountId::from_eth(owner);395 let spender = T::CrossAccountId::from_eth(spender);396397 Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())398 }399}400401#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]402impl<T: Config> CollectionHandle<T> {}403404macro_rules! generate_code {405 ($name:ident, $decl:ident, $is_impl:literal) => {406 #[test]407 #[ignore]408 fn $name() {409 use sp_std::collections::btree_set::BTreeSet;410 let mut out = BTreeSet::new();411 $decl::generate_solidity_interface(&mut out, $is_impl);412 println!("=== SNIP START ===");413 println!("// SPDX-License-Identifier: OTHER");414 println!("// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::{} --exact --nocapture --ignored`", stringify!(name));415 println!();416 println!("pragma solidity >=0.8.0 <0.9.0;");417 println!();418 for b in out {419 println!("{}", b);420 }421 println!("=== SNIP END ===");422 }423 };424}425426// Not a tests, but code generators427generate_code!(nft_impl, UniqueNFTCall, true);428generate_code!(nft_iface, UniqueNFTCall, false);429430generate_code!(fungible_impl, UniqueFungibleCall, true);431generate_code!(fungible_iface, UniqueFungibleCall, false);pallets/nft/src/eth/stubs/ERC721.soldiffbeforeafterboth--- a/pallets/nft/src/eth/stubs/ERC721.sol
+++ b/pallets/nft/src/eth/stubs/ERC721.sol
@@ -140,7 +140,7 @@
}
contract ERC721Metadata is Dummy, InlineNameSymbol {
- function tokenUri(uint256 tokenId) public view returns (string memory) {
+ function tokenURI(uint256 tokenId) public view returns (string memory) {
require(false, stub_error);
tokenId;
dummy;