git.delta.rocks / unique-network / refs/commits / fa23d159ee84

difftreelog

source

pallets/nft/src/eth/mod.rs5.1 KiBsourcehistory
1pub mod account;2pub mod erc;3mod erc_impl;4pub mod log;5pub mod sponsoring;67use evm_coder::abi::AbiWriter;8use evm_coder::abi::StringError;9use sp_std::prelude::*;10use sp_std::borrow::ToOwned;11use sp_std::vec::Vec;1213use pallet_evm::{PrecompileOutput, ExitReason, ExitRevert, ExitSucceed};14use sp_core::{H160, U256};15use frame_support::storage::StorageMap;1617use crate::{Config, CollectionById, CollectionHandle, CollectionId, CollectionMode};1819use erc::{UniqueFungible, UniqueFungibleCall, UniqueNFT, UniqueNFTCall};20use evm_coder::{types::*, abi::AbiReader};2122pub struct NftErcSupport<T: Config>(core::marker::PhantomData<T>);2324// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection25// TODO: Unhardcode prefix26const ETH_ACCOUNT_PREFIX: [u8; 16] = [27	0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,28];2930fn map_eth_to_id(eth: &H160) -> Option<CollectionId> {31	if &eth[0..16] != ETH_ACCOUNT_PREFIX {32		return None;33	}34	let mut id_bytes = [0; 4];35	id_bytes.copy_from_slice(&eth[16..20]);36	Some(u32::from_be_bytes(id_bytes))37}38pub fn collection_id_to_address(id: u32) -> H160 {39	let mut out = [0; 20];40	out[0..16].copy_from_slice(&ETH_ACCOUNT_PREFIX);41	out[16..20].copy_from_slice(&u32::to_be_bytes(id));42	H160(out)43}4445fn call_internal<T: Config>(46	collection: &mut CollectionHandle<T>,47	caller: caller,48	method_id: u32,49	mut input: AbiReader,50	value: U256,51) -> Result<Option<AbiWriter>, evm_coder::abi::StringError> {52	match collection.mode.clone() {53		CollectionMode::Fungible(_) => {54			#[cfg(feature = "std")]55			{56				println!("Parse fungible call {:x}", method_id);57			}58			let call = match UniqueFungibleCall::parse(method_id, &mut input)? {59				Some(v) => v,60				None => {61					#[cfg(feature = "std")]62					{63						println!("Method not found");64					}65					return Ok(None);66				}67			};68			#[cfg(feature = "std")]69			{70				dbg!(&call);71			}72			Ok(Some(<CollectionHandle<T> as UniqueFungible>::call(73				collection,74				Msg {75					call,76					caller,77					value,78				},79			)?))80		}81		CollectionMode::NFT => {82			let call = match UniqueNFTCall::parse(method_id, &mut input)? {83				Some(v) => v,84				None => return Ok(None),85			};86			Ok(Some(<CollectionHandle<T> as UniqueNFT>::call(87				collection,88				Msg {89					call,90					caller,91					value,92				},93			)?))94		}95		_ => {96			return Err(StringError::from(97				"erc calls only supported to fungible and nft collections for now",98			)99			.into())100		}101	}102}103104impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {105	fn is_reserved(target: &H160) -> bool {106		map_eth_to_id(target).is_some()107	}108	fn is_used(target: &H160) -> bool {109		map_eth_to_id(target)110			.map(<CollectionById<T>>::contains_key)111			.unwrap_or(false)112	}113	fn get_code(target: &H160) -> Option<Vec<u8>> {114		map_eth_to_id(&target)115			.and_then(<CollectionById<T>>::get)116			.map(|collection| {117				match collection.mode {118					CollectionMode::NFT => include_bytes!("stubs/ERC721.bin") as &[u8],119					CollectionMode::Fungible(_) => include_bytes!("stubs/ERC20.bin") as &[u8],120					CollectionMode::ReFungible => include_bytes!("stubs/ERC1633.bin") as &[u8],121					CollectionMode::Invalid => include_bytes!("stubs/Invalid.bin") as &[u8],122				}123				.to_owned()124			})125	}126	fn call(127		source: &H160,128		target: &H160,129		gas_limit: u64,130		input: &[u8],131		value: U256,132	) -> Option<PrecompileOutput> {133		let mut collection = map_eth_to_id(&target)134			.and_then(|id| <CollectionHandle<T>>::get_with_gas_limit(id, gas_limit))?;135		let (method_id, input) = AbiReader::new_call(input).unwrap();136		let result = call_internal(&mut collection, *source, method_id, input, value);137		let cost = gas_limit - collection.gas_left();138		let logs = collection.logs.retrieve_logs();139		match result {140			Ok(Some(v)) => Some(PrecompileOutput {141				exit_status: ExitReason::Succeed(ExitSucceed::Returned),142				cost,143				logs,144				output: v.finish(),145			}),146			Ok(None) => None,147			Err(e) => Some(PrecompileOutput {148				exit_status: ExitReason::Revert(ExitRevert::Reverted),149				cost: 0,150				logs: Default::default(),151				output: AbiWriter::from(e).finish(),152			}),153		}154	}155}156157// TODO: This function is slow, and output can be memoized158pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction {159	let contract = collection_id_to_address(collection_id);160161	// FIXME: Can be done on wasm runtime with https://github.com/paritytech/substrate/pull/8728162	#[cfg(feature = "std")]163	{164		let signed = ethereum_tx_sign::RawTransaction {165			nonce: 0.into(),166			to: Some(contract.0.into()),167			value: 0.into(),168			gas_price: 0.into(),169			gas: 0.into(),170			// zero selector, this transaction always have same sender, so all data should be acquired from logs171			data: Vec::from([0, 0, 0, 0]),172		}173		.sign(174			// TODO: move to pallet config175			// 0xF70631E55faff9f3FD3681545aa6c724226a3853176			// 9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a177			&hex_literal::hex!("9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a")178				.into(),179			&chain_id,180		);181		rlp::decode::<ethereum::Transaction>(&signed)182			.expect("transaction is just created, it can't be broken")183	}184	#[cfg(not(feature = "std"))]185	{186		panic!("transaction generation not yet supported by wasm runtime")187	}188}