--- a/Cargo.lock +++ b/Cargo.lock @@ -6264,9 +6264,11 @@ "pallet-evm", "pallet-evm-coder-substrate", "pallet-evm-transaction-payment", + "pallet-structure", "parity-scale-codec", "scale-info", "sp-core", + "sp-runtime", "sp-std", "up-data-structs", ] --- a/pallets/balances-adapter/Cargo.toml +++ b/pallets/balances-adapter/Cargo.toml @@ -9,7 +9,9 @@ frame-support = { workspace = true } frame-system = { workspace = true } pallet-balances = { workspace = true } +pallet-structure = { workspace = true } sp-core = { workspace = true } +sp-runtime = { workspace = true } sp-std = { workspace = true } #Parity --- a/pallets/balances-adapter/src/common.rs +++ b/pallets/balances-adapter/src/common.rs @@ -1,13 +1,9 @@ use alloc::{vec, vec::Vec}; use core::marker::PhantomData; -use crate::{Config, NativeFungibleHandle}; -use frame_support::{ - fail, - traits::{Currency, ExistenceRequirement}, - weights::Weight, -}; +use crate::{Config, NativeFungibleHandle, Pallet}; +use frame_support::{fail, traits::Currency, weights::Weight}; use pallet_balances::{weights::SubstrateWeight as BalancesWeight, WeightInfo}; -use pallet_common::{erc::CrossAccountId, CommonCollectionOperations, CommonWeightInfo, with_weight}; +use pallet_common::{erc::CrossAccountId, CommonCollectionOperations, CommonWeightInfo}; use up_data_structs::TokenId; pub struct CommonWeights(PhantomData); @@ -188,17 +184,9 @@ to: ::CrossAccountId, _token: TokenId, amount: u128, - _budget: &dyn up_data_structs::budget::Budget, + budget: &dyn up_data_structs::budget::Budget, ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { - with_weight( - ::Currency::transfer( - sender.as_sub(), - to.as_sub(), - amount.into(), - ExistenceRequirement::KeepAlive, - ), - Default::default(), - ) + >::transfer(self, &sender, &to, amount, budget) } fn approve( @@ -229,20 +217,9 @@ to: ::CrossAccountId, _token: TokenId, amount: u128, - _budget: &dyn up_data_structs::budget::Budget, + budget: &dyn up_data_structs::budget::Budget, ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { - if sender != from { - fail!(>::NoPermission); - } - with_weight( - ::Currency::transfer( - from.as_sub(), - to.as_sub(), - amount.into(), - ExistenceRequirement::KeepAlive, - ), - Default::default(), - ) + >::transfer_from(self, &sender, &from, &to, amount, budget) } fn burn_from( --- a/pallets/balances-adapter/src/erc.rs +++ b/pallets/balances-adapter/src/erc.rs @@ -1,4 +1,4 @@ -use crate::{Config, NativeFungibleHandle, SelfWeightOf}; +use crate::{Config, NativeFungibleHandle, Pallet, SelfWeightOf}; use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*}; use frame_support::traits::{Currency, ExistenceRequirement}; use pallet_balances::WeightInfo; @@ -10,8 +10,9 @@ use pallet_evm_coder_substrate::{ call, dispatch_to_evm, execution::{PreDispatch, Result}, - frontier_contract, + frontier_contract, WithRecorder, }; +use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _}; use sp_core::{U256, Get}; use sp_std::vec::Vec; @@ -73,18 +74,12 @@ 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()); + let budget = self + .recorder() + .weight_calls_budget(>::find_parent()); - // >::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?; - ::Currency::transfer( - caller.as_sub(), - to.as_sub(), - amount, - ExistenceRequirement::KeepAlive, - ) - .map_err(dispatch_to_evm::)?; + >::transfer(self, &caller, &to, amount, &budget) + .map_err(|e| dispatch_to_evm::(e.error))?; Ok(true) } @@ -100,23 +95,12 @@ let from = T::CrossAccountId::from_eth(from); let to = T::CrossAccountId::from_eth(to); let amount = amount.try_into().map_err(|_| "amount overflow")?; - - if from != caller { - return Err("no permission".into()); - } - // let budget = self - // .recorder - // .weight_calls_budget(>::find_parent()); + let budget = self + .recorder() + .weight_calls_budget(>::find_parent()); - // >::transfer_from(self, &caller, &from, &to, amount, &budget) - // .map_err(dispatch_to_evm::)?; - ::Currency::transfer( - caller.as_sub(), - to.as_sub(), - amount, - ExistenceRequirement::KeepAlive, - ) - .map_err(dispatch_to_evm::)?; + >::transfer_from(self, &caller, &from, &to, amount, &budget) + .map_err(|e| dispatch_to_evm::(e.error))?; Ok(true) } } --- a/pallets/balances-adapter/src/lib.rs +++ b/pallets/balances-adapter/src/lib.rs @@ -5,14 +5,17 @@ use core::ops::Deref; use frame_support::sp_runtime::DispatchResult; +use pallet_evm_coder_substrate::{WithRecorder, SubstrateRecorder}; pub use pallet::*; -use pallet_evm_coder_substrate::{WithRecorder, SubstrateRecorder}; pub mod common; pub mod erc; pub(crate) type SelfWeightOf = ::WeightInfo; +const NATIVE_FUNGIBLE_COLLECTION_ID: up_data_structs::CollectionId = + up_data_structs::CollectionId(0); + /// Handle for native fungible collection pub struct NativeFungibleHandle(SubstrateRecorder); impl NativeFungibleHandle { @@ -45,14 +48,27 @@ } #[frame_support::pallet] pub mod pallet { + use super::*; use alloc::string::String; - use frame_support::{traits::Get}; + use frame_support::{ + dispatch::PostDispatchInfo, + ensure, + pallet_prelude::{DispatchResultWithPostInfo, Pays}, + traits::{Currency, ExistenceRequirement, Get}, + }; use pallet_balances::WeightInfo; + use pallet_common::{erc::CrossAccountId, Error as CommonError, Pallet as PalletCommon}; + use pallet_structure::Pallet as PalletStructure; use sp_core::U256; + use sp_runtime::DispatchError; + use up_data_structs::{budget::Budget, mapping::TokenAddressMapping, TokenId}; #[pallet::config] pub trait Config: - frame_system::Config + pallet_evm_coder_substrate::Config + pallet_common::Config + frame_system::Config + + pallet_evm_coder_substrate::Config + + pallet_common::Config + + pallet_structure::Config { /// Currency from `pallet_balances` type Currency: frame_support::traits::Currency< @@ -74,4 +90,104 @@ } #[pallet::pallet] pub struct Pallet(_); + + impl Pallet { + /// Checks if a non-owner has (enough) allowance from the owner to perform operations on the tokens. + /// Returns the expected remaining allowance - it should be set manually if the transaction proceeds. + /// + /// - `collection`: Collection that contains the token. + /// - `spender`: CrossAccountId who has the allowance rights. + /// - `from`: The owner of the tokens who sets the allowance. + /// - `amount`: Amount of tokens by which the allowance sholud be reduced. + fn check_allowed( + spender: &T::CrossAccountId, + from: &T::CrossAccountId, + nesting_budget: &dyn Budget, + ) -> Result { + if spender.conv_eq(from) { + return Ok(0); + } + + if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) { + ensure!( + >::check_indirectly_owned( + spender.clone(), + source.0, + source.1, + None, + nesting_budget + )?, + >::ApprovedValueTooLow, + ); + } else if spender != from { + return Ok(0); + } + + Ok(::Currency::free_balance(from.as_sub()).into()) + } + + /// Transfers the specified amount of tokens. Will check that + /// the transfer is allowed for the token. + /// + /// - `from`: Owner of tokens to transfer. + /// - `to`: Recepient of transfered tokens. + /// - `amount`: Amount of tokens to transfer. + /// - `collection`: Collection that contains the token + pub fn transfer( + _collection: &NativeFungibleHandle, + from: &T::CrossAccountId, + to: &T::CrossAccountId, + amount: u128, + nesting_budget: &dyn Budget, + ) -> DispatchResultWithPostInfo { + >::ensure_correct_receiver(to)?; + + if from != to && amount != 0 { + ::Currency::transfer( + from.as_sub(), + to.as_sub(), + amount.into(), + ExistenceRequirement::KeepAlive, + )?; + + >::nest_if_sent_to_token( + from.clone(), + to, + NATIVE_FUNGIBLE_COLLECTION_ID, + TokenId::default(), + nesting_budget, + )?; + + let balance_from: u128 = + ::Currency::free_balance(from.as_sub()).into(); + if balance_from == 0 { + >::unnest_if_nested( + from, + NATIVE_FUNGIBLE_COLLECTION_ID, + TokenId::default(), + ); + } + }; + + Ok(PostDispatchInfo { + actual_weight: Some(>::transfer()), + pays_fee: Pays::Yes, + }) + } + + pub fn transfer_from( + collection: &NativeFungibleHandle, + spender: &T::CrossAccountId, + from: &T::CrossAccountId, + to: &T::CrossAccountId, + amount: u128, + nesting_budget: &dyn Budget, + ) -> DispatchResultWithPostInfo { + let allowance = Self::check_allowed(collection, spender, from, amount, nesting_budget)?; + if allowance < amount { + return Err(>::ApprovedValueTooLow.into()); + } + Self::transfer(collection, from, to, amount, nesting_budget) + } + } }