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>);23242526const 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(ð[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(Ð_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}153154155pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction {156 157 #[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 167 data: Vec::from([0, 0, 0, 0]),168 }169 .sign(170 171 172 173 &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}