git.delta.rocks / unique-network / refs/commits / 8e8a17552328

difftreelog

source

pallets/nft/src/eth/mod.rs5.1 KiBsourcehistory
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 &eth[0..16] != ETH_ACCOUNT_PREFIX {26		return None;27	}28	let mut id_bytes = [0; 4];29	id_bytes.copy_from_slice(&eth[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(&ETH_ACCOUNT_PREFIX);35	out[16..20].copy_from_slice(&u32::to_be_bytes(id));36	H160(out)37}3839fn call_internal<T: Config>(40	collection: &mut CollectionHandle<T>,41	caller: caller,42	method_id: u32,43	mut input: AbiReader,44	value: U256,45) -> Result<Option<AbiWriter>, evm_coder::abi::StringError> {46	match collection.mode.clone() {47		CollectionMode::Fungible(_) => {48			#[cfg(feature = "std")]49			{50				println!("Parse fungible call {:x}", method_id);51			}52			let call = match UniqueFungibleCall::parse(method_id, &mut input)? {53				Some(v) => v,54				None => {55					#[cfg(feature = "std")]56					{57						println!("Method not found");58					}59					return Ok(None);60				}61			};62			#[cfg(feature = "std")]63			{64				dbg!(&call);65			}66			Ok(Some(<CollectionHandle<T> as UniqueFungible>::call(67				collection,68				Msg {69					call,70					caller,71					value,72				},73			)?))74		}75		CollectionMode::NFT => {76			let call = match UniqueNFTCall::parse(method_id, &mut input)? {77				Some(v) => v,78				None => return Ok(None),79			};80			Ok(Some(<CollectionHandle<T> as UniqueNFT>::call(81				collection,82				Msg {83					call,84					caller,85					value,86				},87			)?))88		}89		_ => {90			return Err(StringError::from(91				"erc calls only supported to fungible and nft collections for now",92			)93			.into())94		}95	}96}9798impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {99	fn is_reserved(target: &H160) -> bool {100		map_eth_to_id(target).is_some()101	}102	fn is_used(target: &H160) -> bool {103		map_eth_to_id(target)104			.map(<CollectionById<T>>::contains_key)105			.unwrap_or(false)106	}107	fn get_code(target: &H160) -> Option<Vec<u8>> {108		map_eth_to_id(&target)109			.and_then(<CollectionById<T>>::get)110			.map(|collection| {111				match collection.mode {112					CollectionMode::NFT => include_bytes!("stubs/ERC721.bin") as &[u8],113					CollectionMode::Fungible(_) => include_bytes!("stubs/ERC20.bin") as &[u8],114					CollectionMode::ReFungible => include_bytes!("stubs/ERC1633.bin") as &[u8],115					CollectionMode::Invalid => include_bytes!("stubs/Invalid.bin") as &[u8],116				}.to_owned()117			})118	}119	fn call(120		source: &H160,121		target: &H160,122		gas_limit: u64,123		input: &[u8],124		value: U256,125	) -> Option<PrecompileOutput> {126		let mut collection = map_eth_to_id(&target)127			.and_then(|id| <CollectionHandle<T>>::get_with_gas_limit(id, gas_limit))?;128		let (method_id, input) = AbiReader::new_call(input).unwrap();129		let result = call_internal(&mut collection, *source, method_id, input, value);130		let cost = gas_limit - collection.gas_left();131		let logs = collection.logs.retrieve_logs();132		match result {133			Ok(Some(v)) => Some(PrecompileOutput {134				exit_status: ExitReason::Succeed(ExitSucceed::Returned),135				cost,136				logs,137				output: v.finish(),138			}),139			Ok(None) => None,140			Err(e) => Some(PrecompileOutput {141				exit_status: ExitReason::Revert(ExitRevert::Reverted),142				cost: 0,143				logs: Default::default(),144				output: AbiWriter::from(e).finish(),145			}),146		}147	}148}149150// TODO: This function is slow, and output can be memoized151pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction {152	let contract = collection_id_to_address(collection_id);153154	// TODO: Make it work without native runtime by forking ethereum_tx_sign, and155	// switching to pure-rust implementation of secp256k1156	#[cfg(feature = "std")]157	{158		let signed = ethereum_tx_sign::RawTransaction {159			nonce: 0.into(),160			to: Some(contract.0.into()),161			value: 0.into(),162			gas_price: 0.into(),163			gas: 0.into(),164			// zero selector, this transaction always have same sender, so all data should be acquired from logs165			data: Vec::from([0, 0, 0, 0]),166		}.sign(167			// TODO: move to pallet config168			// 0xF70631E55faff9f3FD3681545aa6c724226a3853169			// 9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a170			&hex_literal::hex!("9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a").into(),171			&chain_id172		);173		rlp::decode::<ethereum::Transaction>(&signed).expect("transaction is just created, it can't be broken")174	}175	#[cfg(not(feature = "std"))]176	{177		panic!("transaction generation not yet supported by wasm runtime")178	}179}