difftreelog
Merge commit 'b3fda41c65ef3d5198749d60622f0184af244734' into feature/NFTPAR-366_upstream_updates
in: master
3 files changed
pallets/nft/src/eth/mod.rsdiffbeforeafterboth1pub mod account;2use account::CrossAccountId;3pub mod abi;4use abi::{AbiReader, AbiWriter};5pub mod log;67use sp_std::borrow::ToOwned;8use sp_std::vec::Vec;9use sp_std::convert::TryInto;1011use codec::{Decode, Encode};12use pallet_evm::{AddressMapping, PrecompileLog, PrecompileOutput, ExitReason, ExitRevert, ExitSucceed};13use sp_core::{H160, H256};14use frame_support::storage::{StorageMap, StorageDoubleMap};1516use crate::{Allowances, NftItemList, Module, Balance, Config, CollectionById, CollectionHandle, CollectionId, CollectionMode};1718pub struct NftErcSupport<T: Config>(core::marker::PhantomData<T>);1920// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection21// TODO: Unhardcode prefix22const ETH_ACCOUNT_PREFIX: [u8; 16] = [0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e];2324fn map_eth_to_id(eth: &H160) -> Option<CollectionId> {25 if ð[0..16] != ETH_ACCOUNT_PREFIX {26 return None;27 }28 let mut id_bytes = [0; 4];29 id_bytes.copy_from_slice(ð[16..20]);30 Some(u32::from_be_bytes(id_bytes))31}32pub fn collection_id_to_address(id: u32) -> H160 {33 let mut out = [0; 20];34 out[0..16].copy_from_slice(Ð_ACCOUNT_PREFIX);35 out[16..20].copy_from_slice(&u32::to_be_bytes(id));36 H160(out)37}3839fn result_to_output(result: Result<AbiWriter, Option<&'static str>>, logs: Vec<PrecompileLog>) -> PrecompileOutput {40 sp_io::storage::start_transaction();41 match result {42 Ok(result) => {43 sp_io::storage::commit_transaction();44 // TODO: weight45 PrecompileOutput(ExitReason::Succeed(ExitSucceed::Returned), result.finish(), 0, logs)46 }47 Err(Some(s)) => {48 sp_io::storage::rollback_transaction();49 // Error(string)50 let mut out = AbiWriter::new_call(0x08c379a0);51 out.string(&s);52 PrecompileOutput(ExitReason::Revert(ExitRevert::Reverted), out.finish(), 0, Vec::new())53 }54 Err(None) => {55 sp_io::storage::rollback_transaction();56 PrecompileOutput(ExitReason::Revert(ExitRevert::Reverted), Vec::new(), 0, Vec::new())57 }58 }59}6061fn call_internal<T: Config>(sender: H160, collection: &CollectionHandle<T>, method_id: u32, mut input: AbiReader) -> Result<AbiWriter, Option<&'static str>> {62 let erc20 = matches!(collection.mode, CollectionMode::Fungible(_));63 let erc721 = matches!(collection.mode, CollectionMode::ReFungible);6465 Ok(match method_id {66 // function name() external view returns (string memory)67 0x06fdde03 => {68 let name = collection.name.iter()69 .map(|&e| e.try_into().ok() as Option<u8>)70 .collect::<Option<Vec<u8>>>()71 .ok_or(Some("non-ascii name"))?;72 73 crate::abi_encode!(memory(&name))74 }75 // function symbol() external view returns (string memory)76 0x95d89b41 => {77 let name = collection.token_prefix.iter()78 .map(|&e| e.is_ascii_uppercase().then(|| e))79 .collect::<Option<Vec<u8>>>()80 .ok_or(Some("non-uppercase prefix"))?;81 82 crate::abi_encode!(memory(&name))83 }84 // function decimals() external view returns (uint8 decimals)85 0x313ce567 if erc20 => {86 if let CollectionMode::Fungible(decimals) = &collection.mode {87 crate::abi_encode!(uint8(*decimals))88 } else {89 unreachable!()90 } 91 }92 // function totalSupply() external view returns (uint256)93 0x18160ddd if erc20 => {94 // TODO: can't be implemented, as we don't track total amount of fungibles95 crate::abi_encode!(uint256(0))96 }97 // function balanceOf(address account) external view returns (uint256)98 0x70a08231 if erc20 || erc721 => {99 crate::abi_decode!(input, account: address);100 let account = T::EvmAddressMapping::into_account_id(account);101 let balance = <Balance<T>>::get(collection.id, account);102 crate::abi_encode!(uint256(balance))103 }104 // function ownerOf(uint256 tokenId) external view returns (address)105 0x6352211e if erc721 => {106 crate::abi_decode!(input, token_id: uint256);107 let token_id: u32 = token_id.try_into().map_err(|_| "bad token id")?;108109 let token = <NftItemList<T>>::get(collection.id, token_id).ok_or("unknown token")?;110111 crate::abi_encode!(address(token.owner.as_eth().clone()))112 }113 // function transfer(address recipient, uint256 amount) external returns (bool) {114 0xa9059cbb if erc20 => {115 crate::abi_decode!(input, recipient: address, amount: uint256);116 let sender = T::CrossAccountId::from_eth(sender);117 let recipient = T::CrossAccountId::from_eth(recipient);118119 <Module<T>>::transfer_internal(120 sender,121 recipient,122 &collection,123 1,124 amount,125 ).map_err(|_| "transfer error")?;126127 crate::abi_encode!(bool(true))128 }129 // function allowance(address owner, address spender) external view returns (uint256)130 0xdd62ed3e if erc20 => {131 crate::abi_decode!(input, owner: address, spender: address);132 let owner = T::EvmAddressMapping::into_account_id(owner);133 let spender = T::EvmAddressMapping::into_account_id(spender);134 let allowance = <Allowances<T>>::get(collection.id, (1, &owner, &spender));135 crate::abi_encode!(uint256(allowance))136 }137 // function approve(address spender, uint256 amount) external returns (bool)138 // FIXME: All current implementations resets amount to specified value, ours - adds it139 // FIXME: Our implementation doesn't handle resets (approve with zero amount)140 0x095ea7b3 if erc20 => {141 crate::abi_decode!(input, spender: address, amount: uint256);142 let sender = T::CrossAccountId::from_eth(sender);143 let spender = T::CrossAccountId::from_eth(spender);144145 <Module<T>>::approve_internal(146 sender,147 spender,148 &collection,149 1,150 amount,151 ).map_err(|_| "approve error")?;152153 crate::abi_encode!(bool(true))154 }155 // function approve(address approved, uint256 tokenId) external payable156 0x095ea7b3 if erc721 => {157 crate::abi_decode!(input, approved: address, token_id: uint256);158 let sender = T::CrossAccountId::from_eth(sender);159 let approved = T::CrossAccountId::from_eth(approved);160 let token_id = token_id.try_into().map_err(|_| "bad token id")?;161162 <Module<T>>::approve_internal(163 sender,164 approved,165 &collection,166 token_id,167 1,168 ).map_err(|_| "approve error")?;169 crate::abi_encode!()170 }171 // function transferFrom(address sender, address recipient, uint256 amount) external returns (bool)172 0x23b872dd if erc20 => {173 crate::abi_decode!(input, from: address, recipient: address, amount: uint256);174 let sender = T::CrossAccountId::from_eth(sender);175 let from = T::CrossAccountId::from_eth(from);176 let recipient = T::CrossAccountId::from_eth(recipient);177178 <Module<T>>::transfer_from_internal(179 sender,180 from,181 recipient,182 &collection,183 1,184 amount,185 ).map_err(|_| "transfer_from error")?;186187 crate::abi_encode!(bool(true))188 }189 // function transferFrom(address from, address to, uint256 tokenId) external payable190 0x23b872dd if erc721 => {191 crate::abi_decode!(input, from: address, recipient: address, token_id: uint256);192 let sender = T::CrossAccountId::from_eth(sender);193 let from = T::CrossAccountId::from_eth(from);194 let recipient = T::CrossAccountId::from_eth(recipient);195 let token_id = token_id.try_into().map_err(|_| "bad token id")?;196197 <Module<T>>::transfer_from_internal(198 sender,199 from,200 recipient,201 &collection,202 token_id,203 1,204 ).map_err(|_| "transfer_from error")?;205206 crate::abi_encode!()207 }208 // function supportsInterface(bytes4 interfaceID) public pure returns (bool)209 0x01ffc9a7 => {210 crate::abi_decode!(input, interface_id: uint32);211 let supports = match interface_id {212 // ERC165213 0x01ffc9a7 => true,214 // ERC20215 0x36372b07 if erc20 => true,216 // ERC721217 0x80ac58cd if erc721 => true,218 _ => false,219 };220 crate::abi_encode!(bool(supports))221 }222 _ => return Err(None)223 })224}225226impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {227 fn is_reserved(target: &H160) -> bool {228 map_eth_to_id(target).is_some()229 }230 fn is_used(target: &H160) -> bool {231 map_eth_to_id(target)232 .map(<CollectionById<T>>::contains_key)233 .unwrap_or(false)234 }235 fn get_code(target: &H160) -> Option<Vec<u8>> {236 map_eth_to_id(&target)237 .and_then(<CollectionById<T>>::get)238 .map(|collection| {239 match collection.mode {240 CollectionMode::NFT => include_bytes!("stubs/ERC721.bin") as &[u8],241 CollectionMode::Fungible(_) => include_bytes!("stubs/ERC20.bin") as &[u8],242 CollectionMode::ReFungible => include_bytes!("stubs/ERC1633.bin") as &[u8],243 CollectionMode::Invalid => include_bytes!("stubs/Invalid.bin") as &[u8],244 }.to_owned()245 })246 }247 fn call(248 source: &H160,249 target: &H160,250 input: &[u8],251 ) -> Option<PrecompileOutput> {252 let collection = map_eth_to_id(&target)253 .and_then(<CollectionHandle<T>>::get)?;254 let (method_id, input) = AbiReader::new_call(input).unwrap();255 let result = call_internal(*source, &collection, method_id, input);256 Some(result_to_output(result, collection.logs.retrieve_logs_for_contract(*target)))257 }258}259260/// event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);261pub const TRANSFER_NFT_TOPIC: H256 = H256(hex_literal::hex!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"));262/// event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);263pub const APPROVAL_NFT_TOPIC: H256 = H256(hex_literal::hex!("8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"));264// TODO: event ApprovalForAll(address indexed owner, address indexed operator, bool approved);265266/// event Transfer(address indexed from, address indexed to, uint256 indexed amount);267pub const TRANSFER_FUNGIBLE_TOPIC: H256 = H256(hex_literal::hex!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"));268/// event Approval(address indexed owner, address indexed approved, uint256 indexed amount);269pub const APPROVAL_FUNGIBLE_TOPIC: H256 = H256(hex_literal::hex!("8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"));270271pub fn address_to_topic(address: &H160) -> H256 {272 let mut output = [0; 32];273 output[12..32].copy_from_slice(&address.0);274 H256(output)275}276277278// TODO: This function is slow, and output can be memoized279pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction {280 let contract = collection_id_to_address(collection_id);281282 // TODO: Make it work without native runtime by forking ethereum_tx_sign, and283 // switching to pure-rust implementation of secp256k1284 #[cfg(feature = "std")]285 {286 let signed = ethereum_tx_sign::RawTransaction {287 nonce: 0.into(),288 to: Some(contract.0.into()),289 value: 0.into(),290 gas_price: 0.into(),291 gas: 0.into(),292 // zero selector, this transaction always have same sender, so all data should be acquired from logs293 data: Vec::from([0, 0, 0, 0]),294 }.sign(295 // TODO: move to pallet config296 // 0xF70631E55faff9f3FD3681545aa6c724226a3853297 // 9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a298 &hex_literal::hex!("9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a").into(),299 &chain_id300 );301 rlp::decode::<ethereum::Transaction>(&signed).expect("transaction is just created, it can't be broken")302 }303 #[cfg(not(feature = "std"))]304 {305 panic!("transaction generation not yet supported by wasm runtime")306 }307}1pub mod account;2use account::CrossAccountId;3pub mod abi;4use abi::{AbiReader, AbiWriter};5pub mod log;67use sp_std::borrow::ToOwned;8use sp_std::vec::Vec;9use sp_std::convert::TryInto;1011use codec::{Decode, Encode};12use pallet_evm::{AddressMapping, PrecompileLog, PrecompileOutput, ExitReason, ExitRevert, ExitSucceed};13use sp_core::{H160, H256};14use frame_support::storage::{StorageMap, StorageDoubleMap};1516use crate::{Allowances, NftItemList, Module, Balance, Config, CollectionById, CollectionHandle, CollectionId, CollectionMode};1718pub struct NftErcSupport<T: Config>(core::marker::PhantomData<T>);1920// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection21// TODO: Unhardcode prefix22const ETH_ACCOUNT_PREFIX: [u8; 16] = [0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e];2324fn map_eth_to_id(eth: &H160) -> Option<CollectionId> {25 if ð[0..16] != ETH_ACCOUNT_PREFIX {26 return None;27 }28 let mut id_bytes = [0; 4];29 id_bytes.copy_from_slice(ð[16..20]);30 Some(u32::from_be_bytes(id_bytes))31}32pub fn collection_id_to_address(id: u32) -> H160 {33 let mut out = [0; 20];34 out[0..16].copy_from_slice(Ð_ACCOUNT_PREFIX);35 out[16..20].copy_from_slice(&u32::to_be_bytes(id));36 H160(out)37}3839fn result_to_output(result: Result<AbiWriter, Option<&'static str>>, logs: Vec<PrecompileLog>) -> PrecompileOutput {40 sp_io::storage::start_transaction();41 match result {42 Ok(result) => {43 sp_io::storage::commit_transaction();44 // TODO: weight45 PrecompileOutput(ExitReason::Succeed(ExitSucceed::Returned), result.finish(), 0, logs)46 }47 Err(Some(s)) => {48 sp_io::storage::rollback_transaction();49 // Error(string)50 let mut out = AbiWriter::new_call(0x08c379a0);51 out.string(&s);52 PrecompileOutput(ExitReason::Revert(ExitRevert::Reverted), out.finish(), 0, Vec::new())53 }54 Err(None) => {55 sp_io::storage::rollback_transaction();56 PrecompileOutput(ExitReason::Revert(ExitRevert::Reverted), Vec::new(), 0, Vec::new())57 }58 }59}6061fn call_internal<T: Config>(sender: H160, collection: &CollectionHandle<T>, method_id: u32, mut input: AbiReader) -> Result<AbiWriter, Option<&'static str>> {62 let erc20 = matches!(collection.mode, CollectionMode::Fungible(_));63 let erc721 = matches!(collection.mode, CollectionMode::ReFungible);6465 Ok(match method_id {66 // function name() external view returns (string memory)67 0x06fdde03 => {68 let name = collection.name.iter()69 .map(|&e| e.try_into().ok() as Option<u8>)70 .collect::<Option<Vec<u8>>>()71 .ok_or(Some("non-ascii name"))?;72 73 crate::abi_encode!(memory(&name))74 }75 // function symbol() external view returns (string memory)76 0x95d89b41 => {77 let name = collection.token_prefix.iter()78 .map(|&e| e.is_ascii_uppercase().then(|| e))79 .collect::<Option<Vec<u8>>>()80 .ok_or(Some("non-uppercase prefix"))?;81 82 crate::abi_encode!(memory(&name))83 }84 // function decimals() external view returns (uint8 decimals)85 0x313ce567 if erc20 => {86 if let CollectionMode::Fungible(decimals) = &collection.mode {87 crate::abi_encode!(uint8(*decimals))88 } else {89 unreachable!()90 } 91 }92 // function totalSupply() external view returns (uint256)93 0x18160ddd if erc20 => {94 // TODO: can't be implemented, as we don't track total amount of fungibles95 crate::abi_encode!(uint256(0))96 }97 // function balanceOf(address account) external view returns (uint256)98 0x70a08231 if erc20 || erc721 => {99 crate::abi_decode!(input, account: address);100 let account = T::EvmAddressMapping::into_account_id(account);101 let balance = <Balance<T>>::get(collection.id, account);102 crate::abi_encode!(uint256(balance))103 }104 // function ownerOf(uint256 tokenId) external view returns (address)105 0x6352211e if erc721 => {106 crate::abi_decode!(input, token_id: uint256);107 let token_id: u32 = token_id.try_into().map_err(|_| "bad token id")?;108109 let token = <NftItemList<T>>::get(collection.id, token_id).ok_or("unknown token")?;110111 crate::abi_encode!(address(token.owner.as_eth().clone()))112 }113 // function transfer(address recipient, uint256 amount) external returns (bool) {114 0xa9059cbb if erc20 => {115 crate::abi_decode!(input, recipient: address, amount: uint256);116 let sender = T::CrossAccountId::from_eth(sender);117 let recipient = T::CrossAccountId::from_eth(recipient);118119 <Module<T>>::transfer_internal(120 &sender,121 &recipient,122 &collection,123 1,124 amount,125 ).map_err(|_| "transfer error")?;126127 crate::abi_encode!(bool(true))128 }129 // function allowance(address owner, address spender) external view returns (uint256)130 0xdd62ed3e if erc20 => {131 crate::abi_decode!(input, owner: address, spender: address);132 let owner = T::EvmAddressMapping::into_account_id(owner);133 let spender = T::EvmAddressMapping::into_account_id(spender);134 let allowance = <Allowances<T>>::get(collection.id, (1, &owner, &spender));135 crate::abi_encode!(uint256(allowance))136 }137 // function approve(address spender, uint256 amount) external returns (bool)138 // FIXME: All current implementations resets amount to specified value, ours - adds it139 // FIXME: Our implementation doesn't handle resets (approve with zero amount)140 0x095ea7b3 if erc20 => {141 crate::abi_decode!(input, spender: address, amount: uint256);142 let sender = T::CrossAccountId::from_eth(sender);143 let spender = T::CrossAccountId::from_eth(spender);144145 <Module<T>>::approve_internal(146 &sender,147 &spender,148 &collection,149 1,150 amount,151 ).map_err(|_| "approve error")?;152153 crate::abi_encode!(bool(true))154 }155 // function approve(address approved, uint256 tokenId) external payable156 0x095ea7b3 if erc721 => {157 crate::abi_decode!(input, approved: address, token_id: uint256);158 let sender = T::CrossAccountId::from_eth(sender);159 let approved = T::CrossAccountId::from_eth(approved);160 let token_id = token_id.try_into().map_err(|_| "bad token id")?;161162 <Module<T>>::approve_internal(163 &sender,164 &approved,165 &collection,166 token_id,167 1,168 ).map_err(|_| "approve error")?;169 crate::abi_encode!()170 }171 // function transferFrom(address sender, address recipient, uint256 amount) external returns (bool)172 0x23b872dd if erc20 => {173 crate::abi_decode!(input, from: address, recipient: address, amount: uint256);174 let sender = T::CrossAccountId::from_eth(sender);175 let from = T::CrossAccountId::from_eth(from);176 let recipient = T::CrossAccountId::from_eth(recipient);177178 <Module<T>>::transfer_from_internal(179 &sender,180 &from,181 &recipient,182 &collection,183 1,184 amount,185 ).map_err(|_| "transfer_from error")?;186187 crate::abi_encode!(bool(true))188 }189 // function transferFrom(address from, address to, uint256 tokenId) external payable190 0x23b872dd if erc721 => {191 crate::abi_decode!(input, from: address, recipient: address, token_id: uint256);192 let sender = T::CrossAccountId::from_eth(sender);193 let from = T::CrossAccountId::from_eth(from);194 let recipient = T::CrossAccountId::from_eth(recipient);195 let token_id = token_id.try_into().map_err(|_| "bad token id")?;196197 <Module<T>>::transfer_from_internal(198 &sender,199 &from,200 &recipient,201 &collection,202 token_id,203 1,204 ).map_err(|_| "transfer_from error")?;205206 crate::abi_encode!()207 }208 // function supportsInterface(bytes4 interfaceID) public pure returns (bool)209 0x01ffc9a7 => {210 crate::abi_decode!(input, interface_id: uint32);211 let supports = match interface_id {212 // ERC165213 0x01ffc9a7 => true,214 // ERC20215 0x36372b07 if erc20 => true,216 // ERC721217 0x80ac58cd if erc721 => true,218 _ => false,219 };220 crate::abi_encode!(bool(supports))221 }222 _ => return Err(None)223 })224}225226impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {227 fn is_reserved(target: &H160) -> bool {228 map_eth_to_id(target).is_some()229 }230 fn is_used(target: &H160) -> bool {231 map_eth_to_id(target)232 .map(<CollectionById<T>>::contains_key)233 .unwrap_or(false)234 }235 fn get_code(target: &H160) -> Option<Vec<u8>> {236 map_eth_to_id(&target)237 .and_then(<CollectionById<T>>::get)238 .map(|collection| {239 match collection.mode {240 CollectionMode::NFT => include_bytes!("stubs/ERC721.bin") as &[u8],241 CollectionMode::Fungible(_) => include_bytes!("stubs/ERC20.bin") as &[u8],242 CollectionMode::ReFungible => include_bytes!("stubs/ERC1633.bin") as &[u8],243 CollectionMode::Invalid => include_bytes!("stubs/Invalid.bin") as &[u8],244 }.to_owned()245 })246 }247 fn call(248 source: &H160,249 target: &H160,250 input: &[u8],251 ) -> Option<PrecompileOutput> {252 let collection = map_eth_to_id(&target)253 .and_then(<CollectionHandle<T>>::get)?;254 let (method_id, input) = AbiReader::new_call(input).unwrap();255 let result = call_internal(*source, &collection, method_id, input);256 Some(result_to_output(result, collection.logs.retrieve_logs_for_contract(*target)))257 }258}259260/// event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);261pub const TRANSFER_NFT_TOPIC: H256 = H256(hex_literal::hex!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"));262/// event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);263pub const APPROVAL_NFT_TOPIC: H256 = H256(hex_literal::hex!("8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"));264// TODO: event ApprovalForAll(address indexed owner, address indexed operator, bool approved);265266/// event Transfer(address indexed from, address indexed to, uint256 indexed amount);267pub const TRANSFER_FUNGIBLE_TOPIC: H256 = H256(hex_literal::hex!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"));268/// event Approval(address indexed owner, address indexed approved, uint256 indexed amount);269pub const APPROVAL_FUNGIBLE_TOPIC: H256 = H256(hex_literal::hex!("8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"));270271pub fn address_to_topic(address: &H160) -> H256 {272 let mut output = [0; 32];273 output[12..32].copy_from_slice(&address.0);274 H256(output)275}276277278// TODO: This function is slow, and output can be memoized279pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction {280 let contract = collection_id_to_address(collection_id);281282 // TODO: Make it work without native runtime by forking ethereum_tx_sign, and283 // switching to pure-rust implementation of secp256k1284 #[cfg(feature = "std")]285 {286 let signed = ethereum_tx_sign::RawTransaction {287 nonce: 0.into(),288 to: Some(contract.0.into()),289 value: 0.into(),290 gas_price: 0.into(),291 gas: 0.into(),292 // zero selector, this transaction always have same sender, so all data should be acquired from logs293 data: Vec::from([0, 0, 0, 0]),294 }.sign(295 // TODO: move to pallet config296 // 0xF70631E55faff9f3FD3681545aa6c724226a3853297 // 9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a298 &hex_literal::hex!("9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a").into(),299 &chain_id300 );301 rlp::decode::<ethereum::Transaction>(&signed).expect("transaction is just created, it can't be broken")302 }303 #[cfg(not(feature = "std"))]304 {305 panic!("transaction generation not yet supported by wasm runtime")306 }307}pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -870,10 +870,14 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = Self::get_collection(collection_id)?;
- Self::check_owner_or_admin_permissions(&collection, &sender)?;
- <WhiteList<T>>::insert(collection_id, address.as_sub(), true);
-
+ Self::toggle_white_list_internal(
+ &sender,
+ &collection,
+ &address,
+ true,
+ )?;
+
Ok(())
}
@@ -895,9 +899,13 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = Self::get_collection(collection_id)?;
- Self::check_owner_or_admin_permissions(&collection, &sender)?;
- <WhiteList<T>>::remove(collection_id, address.as_sub());
+ Self::toggle_white_list_internal(
+ &sender,
+ &collection,
+ &address,
+ false,
+ )?;
Ok(())
}
@@ -1137,16 +1145,12 @@
#[weight = <T as Config>::WeightInfo::create_item(data.len())]
#[transactional]
pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {
-
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
+ let collection = Self::get_collection(collection_id)?;
- let target_collection = Self::get_collection(collection_id)?;
+ Self::create_item_internal(&sender, &collection, &owner, data);
- Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;
- Self::validate_create_item_args(&target_collection, &data)?;
- Self::create_item_no_validation(&target_collection, owner, data)?;
-
- Self::submit_logs(target_collection)?;
+ Self::submit_logs(collection)?;
Ok(())
}
@@ -1178,7 +1182,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = Self::get_collection(collection_id)?;
- Self::create_multiple_items_internal(sender, &collection, owner, items_data)?;
+ Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;
Self::submit_logs(collection)?;
Ok(())
@@ -1239,7 +1243,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = Self::get_collection(collection_id)?;
- Self::transfer_internal(sender, recipient, &collection, item_id, value)?;
+ Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;
Self::submit_logs(collection)?;
Ok(())
@@ -1263,11 +1267,10 @@
#[weight = <T as Config>::WeightInfo::approve()]
#[transactional]
pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {
-
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = Self::get_collection(collection_id)?;
- Self::approve_internal(sender, spender, &collection, item_id, amount)?;
+ Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;
Self::submit_logs(collection)?;
Ok(())
@@ -1295,19 +1298,15 @@
#[weight = <T as Config>::WeightInfo::transfer_from()]
#[transactional]
pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {
-
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = Self::get_collection(collection_id)?;
- Self::transfer_from_internal(sender, from, recipient, &collection, item_id, value)?;
+ Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;
Self::submit_logs(collection)?;
Ok(())
}
-
// #[weight = 0]
- // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {
-
// // let no_perm_mes = "You do not have permissions to modify this collection";
// // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);
// // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));
@@ -1342,8 +1341,9 @@
) -> DispatchResult {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
- let target_collection = Self::get_collection(collection_id)?;
- Self::set_variable_meta_data_internal(sender, &target_collection, item_id, data)?;
+ let collection = Self::get_collection(collection_id)?;
+
+ Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;
Ok(())
}
@@ -1679,8 +1679,15 @@
}
impl<T: Config> Module<T> {
+ pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {
+ Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;
+ Self::validate_create_item_args(&collection, &data)?;
+ Self::create_item_no_validation(&collection, owner, data)?;
+
+ Ok(())
+ }
- pub fn transfer_internal(sender: T::CrossAccountId, recipient: T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {
+ pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {
// Limits check
Self::is_correct_transfer(target_collection, &recipient)?;
@@ -1702,14 +1709,14 @@
_ => ()
};
- Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));
+ Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));
Ok(())
}
pub fn approve_internal(
- sender: T::CrossAccountId,
- spender: T::CrossAccountId,
+ sender: &T::CrossAccountId,
+ spender: &T::CrossAccountId,
collection: &CollectionHandle<T>,
item_id: TokenId,
amount: u128
@@ -1772,14 +1779,14 @@
);
}
- Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender, spender, allowance));
+ Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));
Ok(())
}
pub fn transfer_from_internal(
- sender: T::CrossAccountId,
- from: T::CrossAccountId,
- recipient: T::CrossAccountId,
+ sender: &T::CrossAccountId,
+ from: &T::CrossAccountId,
+ recipient: &T::CrossAccountId,
collection: &CollectionHandle<T>,
item_id: TokenId,
amount: u128,
@@ -1841,7 +1848,7 @@
}
pub fn set_variable_meta_data_internal(
- sender: T::CrossAccountId,
+ sender: &T::CrossAccountId,
collection: &CollectionHandle<T>,
item_id: TokenId,
data: Vec<u8>,
@@ -1867,9 +1874,9 @@
}
pub fn create_multiple_items_internal(
- sender: T::CrossAccountId,
+ sender: &T::CrossAccountId,
collection: &CollectionHandle<T>,
- owner: T::CrossAccountId,
+ owner: &T::CrossAccountId,
items_data: Vec<CreateItemData>,
) -> DispatchResult {
Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;
@@ -1878,7 +1885,7 @@
Self::validate_create_item_args(&collection, data)?;
}
for data in &items_data {
- Self::create_item_no_validation(&collection, owner.clone(), data.clone())?;
+ Self::create_item_no_validation(&collection, owner, data.clone())?;
}
Ok(())
@@ -1914,6 +1921,23 @@
Ok(())
}
+ pub fn toggle_white_list_internal(
+ sender: &T::CrossAccountId,
+ collection: &CollectionHandle<T>,
+ address: &T::CrossAccountId,
+ whitelisted: bool,
+ ) -> DispatchResult {
+ Self::check_owner_or_admin_permissions(&collection, &sender)?;
+
+ if whitelisted {
+ <WhiteList<T>>::insert(collection.id, address.as_sub(), true);
+ } else {
+ <WhiteList<T>>::remove(collection.id, address.as_sub());
+ }
+
+ Ok(())
+ }
+
fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {
let collection_id = collection.id;
@@ -1984,7 +2008,7 @@
Ok(())
}
- fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {
+ fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {
match data
{
CreateItemData::NFT(data) => {
runtime/src/chain_extension.rsdiffbeforeafterboth--- a/runtime/src/chain_extension.rs
+++ b/runtime/src/chain_extension.rs
@@ -17,10 +17,17 @@
extern crate pallet_nft;
pub use pallet_nft::*;
-use crate::Runtime;
-use sp_runtime::AccountId32;
+use pallet_nft::CrossAccountId;
use crate::Vec;
+/// Create item parameters
+#[derive(Debug, PartialEq, Encode, Decode)]
+pub struct NFTExtCreateItem<E: Ext> {
+ pub owner: <E::T as SysConfig>::AccountId,
+ pub collection_id: u32,
+ pub data: CreateItemData,
+}
+
/// Transfer parameters
#[derive(Debug, PartialEq, Encode, Decode)]
pub struct NFTExtTransfer<E: Ext> {
@@ -30,12 +37,51 @@
pub amount: u128,
}
+#[derive(Debug, PartialEq, Encode, Decode)]
+pub struct NFTExtCreateMultipleItems<E: Ext> {
+ pub owner: <E::T as SysConfig>::AccountId,
+ pub collection_id: u32,
+ pub data: Vec<CreateItemData>,
+}
+
+#[derive(Debug, PartialEq, Encode, Decode)]
+pub struct NFTExtApprove<E: Ext> {
+ pub spender: <E::T as SysConfig>::AccountId,
+ pub collection_id: u32,
+ pub item_id: u32,
+ pub amount: u128,
+}
+
+#[derive(Debug, PartialEq, Encode, Decode)]
+pub struct NFTExtTransferFrom<E: Ext> {
+ pub owner: <E::T as SysConfig>::AccountId,
+ pub recipient: <E::T as SysConfig>::AccountId,
+ pub collection_id: u32,
+ pub item_id: u32,
+ pub amount: u128,
+}
+
+#[derive(Debug, PartialEq, Encode, Decode)]
+pub struct NFTExtSetVariableMetaData {
+ pub collection_id: u32,
+ pub item_id: u32,
+ pub data: Vec<u8>,
+}
+
+#[derive(Debug, PartialEq, Encode, Decode)]
+pub struct NFTExtToggleWhiteList<E: Ext> {
+ pub collection_id: u32,
+ pub address: <E::T as SysConfig>::AccountId,
+ pub whitelisted: bool,
+}
+
/// The chain Extension of NFT pallet
pub struct NFTExtension;
impl<C: Config> ChainExtension<C> for NFTExtension {
fn call<E: Ext>(func_id: u32, env: Environment<E, InitState>) -> Result<RetVal, DispatchError>
where
+ E: Ext<T = C>,
<E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
{
// The memory of the vm stores buf in scale-codec
@@ -44,37 +90,118 @@
let mut env = env.buf_in_buf_out();
let input: NFTExtTransfer<E> = env.read_as()?;
- // Sender to AccountId32
- let mut bytes_sender: [u8; 32] = [0; 32];
- let addr_vec_sender: Vec<u8> = env.ext().caller().encode();
- for i in 0..32 {
- bytes_sender[i] = addr_vec_sender[i];
+ let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
+
+ match pallet_nft::Module::<C>::transfer_internal(
+ &C::CrossAccountId::from_sub(env.ext().caller().clone()),
+ &C::CrossAccountId::from_sub(input.recipient),
+ &collection,
+ input.token_id,
+ input.amount,
+ ) {
+ Ok(_) => Ok(RetVal::Converging(func_id)),
+ _ => Err(DispatchError::Other("Transfer error"))
}
- let sender = AccountId32::from(bytes_sender);
+ },
+ 1 => {
+ // Create Item
+ let mut env = env.buf_in_buf_out();
+ let input: NFTExtCreateItem<E> = env.read_as()?;
- // Recipient to AccountId32
- let mut bytes_rec: [u8; 32] = [0; 32];
- let addr_vec_rec: Vec<u8> = input.recipient.encode();
- for i in 0..32 {
- bytes_rec[i] = addr_vec_rec[i];
+ let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
+
+ match pallet_nft::Module::<C>::create_item_internal(
+ &C::CrossAccountId::from_sub(env.ext().address().clone()),
+ &collection,
+ &C::CrossAccountId::from_sub(input.owner),
+ input.data,
+ ) {
+ Ok(_) => Ok(RetVal::Converging(func_id)),
+ _ => Err(DispatchError::Other("CreateItem error"))
}
- let recipient = AccountId32::from(bytes_rec);
+ },
+ 2 => {
+ // Create multiple items
+ let mut env = env.buf_in_buf_out();
+ let input: NFTExtCreateMultipleItems<E> = env.read_as()?;
- let collection = pallet_nft::Module::<Runtime>::get_collection(input.collection_id)?;
+ let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
- match pallet_nft::Module::<Runtime>::transfer_internal(
- <Runtime as Config>::CrossAccountId::from_sub(sender),
- <Runtime as Config>::CrossAccountId::from_sub(recipient),
+ match pallet_nft::Module::<C>::create_multiple_items_internal(
+ &C::CrossAccountId::from_sub(env.ext().address().clone()),
&collection,
- input.token_id,
- input.amount,
+ &C::CrossAccountId::from_sub(input.owner),
+ input.data,
) {
Ok(_) => Ok(RetVal::Converging(func_id)),
- _ => Err(DispatchError::Other("Transfer error"))
+ _ => Err(DispatchError::Other("CreateMultipleItems error"))
}
},
- _ => {
- panic!("Passed unknown func_id to test chain extension: {}", func_id);
+ 3 => {
+ // Approve
+ let mut env = env.buf_in_buf_out();
+ let input: NFTExtApprove<E> = env.read_as()?;
+
+ let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
+
+ pallet_nft::Module::<C>::approve_internal(
+ &C::CrossAccountId::from_sub(env.ext().address().clone()),
+ &C::CrossAccountId::from_sub(input.spender),
+ &collection,
+ input.item_id,
+ input.amount,
+ )?;
+ Ok(RetVal::Converging(func_id))
+ },
+ 4 => {
+ // Transfer from
+ let mut env = env.buf_in_buf_out();
+ let input: NFTExtTransferFrom<E> = env.read_as()?;
+
+ let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
+
+ pallet_nft::Module::<C>::transfer_from_internal(
+ &C::CrossAccountId::from_sub(env.ext().address().clone()),
+ &C::CrossAccountId::from_sub(input.owner),
+ &C::CrossAccountId::from_sub(input.recipient),
+ &collection,
+ input.item_id,
+ input.amount
+ )?;
+ Ok(RetVal::Converging(func_id))
+ },
+ 5 => {
+ // Set variable metadata
+ let mut env = env.buf_in_buf_out();
+ let input: NFTExtSetVariableMetaData = env.read_as()?;
+
+ let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
+
+ pallet_nft::Module::<C>::set_variable_meta_data_internal(
+ &C::CrossAccountId::from_sub(env.ext().address().clone()),
+ &collection,
+ input.item_id,
+ input.data,
+ )?;
+ Ok(RetVal::Converging(func_id))
+ },
+ 6 => {
+ // Toggle whitelist
+ let mut env = env.buf_in_buf_out();
+ let input: NFTExtToggleWhiteList<E> = env.read_as()?;
+
+ let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
+
+ pallet_nft::Module::<C>::toggle_white_list_internal(
+ &C::CrossAccountId::from_sub(env.ext().address().clone()),
+ &collection,
+ &C::CrossAccountId::from_sub(input.address),
+ input.whitelisted,
+ )?;
+ Ok(RetVal::Converging(func_id))
+ }
+ _ => {
+ panic!("Passed unknown func_id to test chain extension: {}", func_id);
}
}
}