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

difftreelog

source

pallets/nft/src/eth/mod.rs5.2 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		_ => Err(StringError::from(96			"erc calls only supported to fungible and nft collections for now",97		)),98	}99}100101impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {102	fn is_reserved(target: &H160) -> bool {103		map_eth_to_id(target).is_some()104	}105	fn is_used(target: &H160) -> bool {106		map_eth_to_id(target)107			.map(<CollectionById<T>>::contains_key)108			.unwrap_or(false)109	}110	fn get_code(target: &H160) -> Option<Vec<u8>> {111		map_eth_to_id(target)112			.and_then(<CollectionById<T>>::get)113			.map(|collection| {114				match collection.mode {115					CollectionMode::NFT => include_bytes!("stubs/ERC721.bin") as &[u8],116					CollectionMode::Fungible(_) => include_bytes!("stubs/ERC20.bin") as &[u8],117					CollectionMode::ReFungible => include_bytes!("stubs/ERC1633.bin") as &[u8],118					CollectionMode::Invalid => include_bytes!("stubs/Invalid.bin") as &[u8],119				}120				.to_owned()121			})122	}123	fn call(124		source: &H160,125		target: &H160,126		gas_limit: u64,127		input: &[u8],128		value: U256,129	) -> Option<PrecompileOutput> {130		let mut collection = map_eth_to_id(target)131			.and_then(|id| <CollectionHandle<T>>::get_with_gas_limit(id, gas_limit))?;132		let (method_id, input) = AbiReader::new_call(input).unwrap();133		let result = call_internal(&mut collection, *source, method_id, input, value);134		let cost = gas_limit - collection.gas_left();135		let logs = collection.logs.retrieve_logs();136		match result {137			Ok(Some(v)) => Some(PrecompileOutput {138				exit_status: ExitReason::Succeed(ExitSucceed::Returned),139				cost,140				logs,141				output: v.finish(),142			}),143			Ok(None) => None,144			Err(e) => Some(PrecompileOutput {145				exit_status: ExitReason::Revert(ExitRevert::Reverted),146				cost: 0,147				logs: Default::default(),148				output: AbiWriter::from(e).finish(),149			}),150		}151	}152}153154// TODO: This function is slow, and output can be memoized155pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction {156	// FIXME: Can be done on wasm runtime with https://github.com/paritytech/substrate/pull/8728157	#[cfg(feature = "std")]158	{159		let contract = collection_id_to_address(collection_id);160		let signed = ethereum_tx_sign::RawTransaction {161			nonce: 0.into(),162			to: Some(contract.0.into()),163			value: 0.into(),164			gas_price: 0.into(),165			gas: 0.into(),166			// zero selector, this transaction always have same sender, so all data should be acquired from logs167			data: Vec::from([0, 0, 0, 0]),168		}169		.sign(170			// TODO: move to pallet config171			// 0xF70631E55faff9f3FD3681545aa6c724226a3853172			// 9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a173			&hex_literal::hex!("9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a")174				.into(),175			&chain_id,176		);177		rlp::decode::<ethereum::Transaction>(&signed)178			.expect("transaction is just created, it can't be broken")179	}180	#[cfg(not(feature = "std"))]181	{182		panic!("transaction generation not yet supported by wasm runtime while generating transaction for collection_id {}, chain_id {}", collection_id, chain_id)183	}184}