--- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -43,7 +43,7 @@ }; use sp_runtime::traits::StaticLookup; use pallet_contracts::chain_extension::UncheckedFrom; -use pallet_evm::AddressMapping; +use pallet_ethereum::EthereumTransactionSender; use pallet_transaction_payment::OnChargeTransaction; #[cfg(test)] @@ -185,7 +185,24 @@ pub struct CollectionHandle { pub id: CollectionId, collection: Collection, + logs: eth::log::LogRecorder, } +impl CollectionHandle { + pub fn get(id: CollectionId) -> Option { + >::get(id) + .map(|collection| Self { + id, + collection, + logs: eth::log::LogRecorder::default(), + }) + } + pub fn log(&self, topics: Vec, data: eth::abi::AbiWriter) { + self.logs.log(topics, data) + } + pub fn into_inner(self) -> Collection { + self.collection.clone() + } +} impl Deref for CollectionHandle { type Target = Collection; @@ -471,6 +488,9 @@ type Currency: Currency; type CollectionCreationPrice: Get<<::Currency as Currency>::Balance>; type TreasuryAccountId: Get; + + type EthereumChainId: Get; + type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender; } #[cfg(feature = "runtime-benchmarks")] @@ -1125,6 +1145,7 @@ Self::validate_create_item_args(&target_collection, &data)?; Self::create_item_no_validation(&target_collection, owner, data)?; + Self::submit_logs(target_collection)?; Ok(()) } @@ -1158,6 +1179,7 @@ Self::create_multiple_items_internal(sender, &collection, owner, items_data)?; + Self::submit_logs(collection)?; Ok(()) } @@ -1183,6 +1205,7 @@ Self::burn_item_internal(&sender, &target_collection, item_id, value)?; + Self::submit_logs(target_collection)?; Ok(()) } @@ -1217,6 +1240,7 @@ Self::transfer_internal(sender, recipient, &collection, item_id, value)?; + Self::submit_logs(collection)?; Ok(()) } @@ -1244,6 +1268,7 @@ Self::approve_internal(sender, spender, &collection, item_id, amount)?; + Self::submit_logs(collection)?; Ok(()) } @@ -1275,6 +1300,7 @@ Self::transfer_from_internal(sender, from, recipient, &collection, item_id, value)?; + Self::submit_logs(collection)?; Ok(()) } @@ -1736,7 +1762,30 @@ } >::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance); + if matches!(collection.mode, CollectionMode::NFT) { + // TODO: NFT: only one owner may exist for token in ERC721 + collection.log( + Vec::from([ + eth::APPROVAL_NFT_TOPIC, + eth::address_to_topic(sender.as_eth()), + eth::address_to_topic(spender.as_eth()), + ]), + abi_encode!(uint256(item_id.into())), + ); + } + if matches!(collection.mode, CollectionMode::Fungible(_)) { + // TODO: NFT: only one owner may exist for token in ERC20 + collection.log( + Vec::from([ + eth::APPROVAL_FUNGIBLE_TOPIC, + eth::address_to_topic(sender.as_eth()), + eth::address_to_topic(spender.as_eth()), + ]), + abi_encode!(uint256(allowance.into())), + ); + } + Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender, spender, allowance)); Ok(()) } @@ -1791,6 +1840,17 @@ _ => () }; + if matches!(collection.mode, CollectionMode::Fungible(_)) { + collection.log( + Vec::from([ + eth::APPROVAL_FUNGIBLE_TOPIC, + eth::address_to_topic(from.as_eth()), + eth::address_to_topic(sender.as_eth()), + ]), + abi_encode!(uint256(allowance.into())), + ); + } + Ok(()) } @@ -2017,6 +2077,14 @@ .ok_or(Error::::NumOverflow)?; >::insert(collection_id, item_owner.as_sub(), new_balance); + collection.log( + Vec::from([ + eth::TRANSFER_NFT_TOPIC, + eth::address_to_topic(&H160::default()), + eth::address_to_topic(item_owner.as_eth()), + ]), + abi_encode!(uint256(current_index.into())), + ); Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner)); Ok(()) } @@ -2103,22 +2171,32 @@ >::remove(collection_id, owner.as_sub()); } + collection.log( + Vec::from([ + eth::TRANSFER_FUNGIBLE_TOPIC, + eth::address_to_topic(owner.as_eth()), + eth::address_to_topic(&H160::default()), + ]), + abi_encode!(uint256(value.into())), + ); Ok(()) } pub fn get_collection(collection_id: CollectionId) -> Result, sp_runtime::DispatchError> { - Ok(>::get(collection_id) - .map(|collection| CollectionHandle { - id: collection_id, - collection - }) + Ok(>::get(collection_id) .ok_or(Error::::CollectionNotFound)?) } fn save_collection(collection: CollectionHandle) { - >::insert(collection.id, collection.collection); + >::insert(collection.id, collection.into_inner()); } + fn submit_logs(collection: CollectionHandle) -> DispatchResult { + T::EthereumTransactionSender::submit_logs_transaction( + eth::generate_transaction(collection.id, T::EthereumChainId::get()), + collection.logs.retrieve_logs_for_contract(eth::collection_id_to_address(collection.id)), + ) + } fn check_owner_permissions(target_collection: &CollectionHandle, subject: &T::CrossAccountId) -> DispatchResult { ensure!( @@ -2224,6 +2302,14 @@ >::insert(collection_id, owner.as_sub(), balance); } + collection.log( + Vec::from([ + eth::TRANSFER_FUNGIBLE_TOPIC, + eth::address_to_topic(owner.as_eth()), + eth::address_to_topic(recipient.as_eth()), + ]), + abi_encode!(uint256(value.into())), + ); Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value)); Ok(()) @@ -2348,6 +2434,14 @@ // update index collection Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?; + collection.log( + Vec::from([ + eth::TRANSFER_NFT_TOPIC, + eth::address_to_topic(sender.as_eth()), + eth::address_to_topic(new_owner.as_eth()), + ]), + abi_encode!(uint256(item_id.into())), + ); Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1)); Ok(()) --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -381,6 +381,7 @@ type Event = Event; type FindAuthor = EthereumFindAuthor; type StateRoot = pallet_ethereum::IntermediateStateRoot; + type EvmSubmitLog = pallet_evm::Module; } impl pallet_grandpa::Config for Runtime { @@ -588,6 +589,9 @@ type Currency = Balances; type CollectionCreationPrice = CollectionCreationPrice; type TreasuryAccountId = TreasuryAccountId; + + type EthereumChainId = ChainId; + type EthereumTransactionSender = pallet_ethereum::Module; } construct_runtime!(