--- a/Cargo.lock +++ b/Cargo.lock @@ -6322,11 +6322,14 @@ name = "pallet-refungible" version = "0.1.1" dependencies = [ + "ethereum", + "evm-coder", "frame-benchmarking", "frame-support", "frame-system", "pallet-common", "pallet-evm", + "pallet-evm-coder-substrate", "pallet-structure", "parity-scale-codec 3.1.5", "scale-info", --- a/pallets/refungible/Cargo.toml +++ b/pallets/refungible/Cargo.toml @@ -11,36 +11,43 @@ version = '3.1.2' [dependencies] -frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } -frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } -sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } -sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } -sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.24" } +evm-coder = { default-features = false, path = '../../crates/evm-coder' } +pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' } pallet-common = { default-features = false, path = '../common' } pallet-structure = { default-features = false, path = '../structure' } +struct-versioning = { path = "../../crates/struct-versioning" } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } + +ethereum = { version = "0.12.0", default-features = false } +scale-info = { version = "2.0.1", default-features = false, features = ["derive",] } + frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } -scale-info = { version = "2.0.1", default-features = false, features = [ - "derive", -] } -struct-versioning = { path = "../../crates/struct-versioning" } +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.24" } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24" } [features] default = ["std"] std = [ + "ethereum/std", + "evm-coder/std", + 'frame-benchmarking/std', "frame-support/std", "frame-system/std", + "pallet-common/std", + "pallet-evm/std", + "pallet-evm-coder-substrate/std", + "pallet-structure/std", "sp-runtime/std", "sp-std/std", "up-data-structs/std", - "pallet-common/std", - "pallet-structure/std", - 'frame-benchmarking/std', - "pallet-evm/std", ] runtime-benchmarks = [ 'frame-benchmarking', 'frame-support/runtime-benchmarks', 'frame-system/runtime-benchmarks', + 'up-data-structs/runtime-benchmarks', ] --- a/pallets/refungible/src/erc.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -use up_data_structs::TokenId; -use pallet_common::erc::CommonEvmHandler; -use pallet_evm::PrecompileHandle; - -use crate::{Config, RefungibleHandle}; - -impl CommonEvmHandler for RefungibleHandle { - const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungible.raw"); - - fn call( - self, - _handle: &mut impl PrecompileHandle, - ) -> Option { - // TODO: Implement RFT variant of ERC721 - None - } -} - -pub struct RefungibleTokenHandle(pub RefungibleHandle, pub TokenId); - -impl CommonEvmHandler for RefungibleTokenHandle { - const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw"); - - fn call( - self, - _handle: &mut impl PrecompileHandle, - ) -> Option { - // TODO: Implement RFT variant of ERC20 - None - } -} --- /dev/null +++ b/pallets/refungible/src/erc20.rs @@ -0,0 +1,204 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +extern crate alloc; +use core::{ + char::{REPLACEMENT_CHARACTER, decode_utf16}, + convert::TryInto, + ops::Deref, +}; +use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight}; +use pallet_common::{ + CommonWeightInfo, + erc::{CommonEvmHandler, PrecompileResult}, +}; +use pallet_evm::{account::CrossAccountId, PrecompileHandle}; +use pallet_evm_coder_substrate::{call, dispatch_to_evm, WithRecorder}; +use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _}; +use sp_std::vec::Vec; +use up_data_structs::{CollectionMode, TokenId}; + +use crate::{ + Allowance, Balance, common::CommonWeights, Config, erc721::UniqueRFTCall, Pallet, + RefungibleHandle, SelfWeightOf, weights::WeightInfo, TotalSupply, +}; + +pub struct RefungibleTokenHandle(pub RefungibleHandle, pub TokenId); + +#[derive(ToLog)] +pub enum ERC20Events { + Transfer { + #[indexed] + from: address, + #[indexed] + to: address, + value: uint256, + }, + Approval { + #[indexed] + owner: address, + #[indexed] + spender: address, + value: uint256, + }, +} + +#[solidity_interface(name = "ERC20", events(ERC20Events))] +impl RefungibleTokenHandle { + fn name(&self) -> Result { + Ok(decode_utf16(self.name.iter().copied()) + .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) + .collect::()) + } + fn symbol(&self) -> Result { + Ok(string::from_utf8_lossy(&self.token_prefix).into()) + } + fn total_supply(&self) -> Result { + self.consume_store_reads(1)?; + Ok(>::get((self.id, self.1)).into()) + } + + fn decimals(&self) -> Result { + Ok(if let CollectionMode::Fungible(decimals) = &self.mode { + *decimals + } else { + unreachable!() + }) + } + fn balance_of(&self, owner: address) -> Result { + self.consume_store_reads(1)?; + let owner = T::CrossAccountId::from_eth(owner); + let balance = >::get((self.id, self.1, owner)); + Ok(balance.into()) + } + #[weight(>::transfer())] + fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let to = T::CrossAccountId::from_eth(to); + let amount = amount.try_into().map_err(|_| "amount overflow")?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + + >::transfer(self, &caller, &to, self.1, amount, &budget) + .map_err(|_| "transfer error")?; + Ok(true) + } + #[weight(>::transfer_from())] + fn transfer_from( + &mut self, + caller: caller, + from: address, + to: address, + amount: uint256, + ) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let from = T::CrossAccountId::from_eth(from); + let to = T::CrossAccountId::from_eth(to); + let amount = amount.try_into().map_err(|_| "amount overflow")?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + + >::transfer_from(self, &caller, &from, &to, self.1, amount, &budget) + .map_err(dispatch_to_evm::)?; + Ok(true) + } + #[weight(>::approve())] + fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let spender = T::CrossAccountId::from_eth(spender); + let amount = amount.try_into().map_err(|_| "amount overflow")?; + + >::set_allowance(self, &caller, &spender, self.1, amount) + .map_err(dispatch_to_evm::)?; + Ok(true) + } + fn allowance(&self, owner: address, spender: address) -> Result { + self.consume_store_reads(1)?; + let owner = T::CrossAccountId::from_eth(owner); + let spender = T::CrossAccountId::from_eth(spender); + + Ok(>::get((self.id, self.1, owner, spender)).into()) + } +} + +#[solidity_interface(name = "ERC20UniqueExtensions")] +impl RefungibleTokenHandle { + #[weight(>::burn_from())] + fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let from = T::CrossAccountId::from_eth(from); + let amount = amount.try_into().map_err(|_| "amount overflow")?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + + >::burn_from(self, &caller, &from, self.1, amount, &budget) + .map_err(dispatch_to_evm::)?; + Ok(true) + } +} + +impl RefungibleTokenHandle { + pub fn into_inner(self) -> RefungibleHandle { + self.0 + } + pub fn common_mut(&mut self) -> &mut RefungibleHandle { + &mut self.0 + } +} + +impl WithRecorder for RefungibleTokenHandle { + fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder { + self.0.recorder() + } + fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder { + self.0.into_recorder() + } +} + +impl Deref for RefungibleTokenHandle { + type Target = RefungibleHandle; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[solidity_interface( + name = "UniqueRefungibleToken", + is( + ERC20, + ERC20UniqueExtensions, + via("RefungibleHandle", common_mut, UniqueRFT) + ) +)] +impl RefungibleTokenHandle where T::AccountId: From<[u8; 32]> {} + +generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true); +generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false); + +impl CommonEvmHandler for RefungibleTokenHandle +where + T::AccountId: From<[u8; 32]>, +{ + const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw"); + + fn call(self, handle: &mut impl PrecompileHandle) -> Option { + call::, _, _>(handle, self) + } +} --- /dev/null +++ b/pallets/refungible/src/erc721.rs @@ -0,0 +1,32 @@ +extern crate alloc; +use evm_coder::{generate_stubgen, solidity_interface, types::*}; + +use pallet_common::{CollectionHandle, erc::CollectionCall, erc::CommonEvmHandler}; + +use pallet_evm::PrecompileHandle; +use pallet_evm_coder_substrate::call; + +use crate::{Config, RefungibleHandle}; + +#[solidity_interface( + name = "UniqueRFT", + is(via("CollectionHandle", common_mut, Collection),) +)] +impl RefungibleHandle where T::AccountId: From<[u8; 32]> {} + +// Not a tests, but code generators +generate_stubgen!(gen_impl, UniqueRFTCall<()>, true); +generate_stubgen!(gen_iface, UniqueRFTCall<()>, false); + +impl CommonEvmHandler for RefungibleHandle +where + T::AccountId: From<[u8; 32]>, +{ + const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungible.raw"); + fn call( + self, + handle: &mut impl PrecompileHandle, + ) -> Option { + call::, _, _>(handle, self) + } +} --- a/pallets/refungible/src/lib.rs +++ b/pallets/refungible/src/lib.rs @@ -87,30 +87,31 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{ensure, fail, BoundedVec, transactional, storage::with_transaction}; -use up_data_structs::{ - AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId, - CreateCollectionData, CreateRefungibleExData, mapping::TokenAddressMapping, budget::Budget, - Property, PropertyScope, TrySetProperty, PropertyKey, PropertyValue, PropertyPermission, - PropertyKeyPermission, -}; -use pallet_evm::account::CrossAccountId; -use pallet_common::{ - Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, - CommonCollectionOperations as _, -}; +use crate::erc20::ERC20Events; + +use codec::{Encode, Decode, MaxEncodedLen}; +use core::ops::Deref; +use evm_coder::ToLog; +use frame_support::{BoundedVec, ensure, fail, storage::with_transaction, transactional}; +use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm}; +use pallet_evm_coder_substrate::WithRecorder; +use pallet_common::{CommonCollectionOperations, Error as CommonError, Event as CommonEvent, Pallet as PalletCommon}; use pallet_structure::Pallet as PalletStructure; +use scale_info::TypeInfo; +use sp_core::H160; use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome}; use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap}; -use core::ops::Deref; -use codec::{Encode, Decode, MaxEncodedLen}; -use scale_info::TypeInfo; +use up_data_structs::{ + AccessMode, budget::Budget, CollectionId, CreateCollectionData, CreateRefungibleExData, CustomDataLimit, mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, TokenId, + Property, PropertyKey, PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue, TrySetProperty +}; pub use pallet::*; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; pub mod common; -pub mod erc; +pub mod erc20; +pub mod erc721; pub mod weights; pub(crate) type SelfWeightOf = ::WeightInfo; @@ -271,7 +272,11 @@ pub fn into_inner(self) -> pallet_common::CollectionHandle { self.0 } + pub fn common_mut(&mut self) -> &mut pallet_common::CollectionHandle { + &mut self.0 + } } + impl Deref for RefungibleHandle { type Target = pallet_common::CollectionHandle; @@ -280,6 +285,15 @@ } } +impl WithRecorder for RefungibleHandle { + fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder { + self.0.recorder() + } + fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder { + self.0.into_recorder() + } +} + impl Pallet { /// Get number of RFT tokens in collection pub fn total_supply(collection: &RefungibleHandle) -> u32 { @@ -460,7 +474,18 @@ >::insert((collection.id, token, owner), balance); } >::insert((collection.id, token), total_supply); - // TODO: ERC20 transfer event + + >::deposit_log( + ERC20Events::Transfer { + from: *owner.as_eth(), + to: H160::default(), + value: amount.into(), + } + .to_log(T::EvmTokenAddressMapping::token_to_address( + collection.id, + token, + )), + ); >::deposit_event(CommonEvent::ItemDestroyed( collection.id, token, @@ -736,7 +761,17 @@ } } - // TODO: ERC20 transfer event + >::deposit_log( + ERC20Events::Transfer { + from: *from.as_eth(), + to: *to.as_eth(), + value: amount.into(), + } + .to_log(T::EvmTokenAddressMapping::token_to_address( + collection.id, + token, + )), + ); >::deposit_event(CommonEvent::Transfer( collection.id, token, @@ -889,7 +924,17 @@ continue; } - // TODO: ERC20 transfer event + >::deposit_log( + ERC20Events::Transfer { + from: H160::default(), + to: *user.as_eth(), + value: amount.into(), + } + .to_log(T::EvmTokenAddressMapping::token_to_address( + collection.id, + TokenId(token_id), + )), + ); >::deposit_event(CommonEvent::ItemCreated( collection.id, TokenId(token_id), @@ -913,7 +958,18 @@ } else { >::insert((collection.id, token, sender, spender), amount); } - // TODO: ERC20 approval event + + >::deposit_log( + ERC20Events::Approval { + owner: *sender.as_eth(), + spender: *spender.as_eth(), + value: amount.into(), + } + .to_log(T::EvmTokenAddressMapping::token_to_address( + collection.id, + token, + )), + ); >::deposit_event(CommonEvent::Approved( collection.id, token, --- a/runtime/common/src/dispatch.rs +++ b/runtime/common/src/dispatch.rs @@ -25,7 +25,7 @@ pub use pallet_common::dispatch::CollectionDispatch; use pallet_fungible::{Pallet as PalletFungible, FungibleHandle}; use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle}; -use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle, erc::RefungibleTokenHandle}; +use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle, erc20::RefungibleTokenHandle}; use up_data_structs::{ CollectionMode, CreateCollectionData, MAX_DECIMAL_POINTS, mapping::TokenAddressMapping, };