From a71170118ff96fe77eb1ffef710fd7619c3cb7ae Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Tue, 13 Sep 2022 22:06:16 +0000 Subject: [PATCH] Merge pull request #570 from UniqueNetwork/feature/sponsoring-fee-limit --- --- a/Cargo.lock +++ b/Cargo.lock @@ -2324,7 +2324,7 @@ [[package]] name = "fc-consensus" version = "2.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "async-trait", "fc-db", @@ -2343,7 +2343,7 @@ [[package]] name = "fc-db" version = "2.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "fp-storage", "kvdb-rocksdb", @@ -2359,7 +2359,7 @@ [[package]] name = "fc-mapping-sync" version = "2.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "fc-db", "fp-consensus", @@ -2376,7 +2376,7 @@ [[package]] name = "fc-rpc" version = "2.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "ethereum-types", @@ -2416,7 +2416,7 @@ [[package]] name = "fc-rpc-core" version = "1.1.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "ethereum-types", @@ -2557,7 +2557,7 @@ [[package]] name = "fp-consensus" version = "2.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "parity-scale-codec 3.1.5", @@ -2569,7 +2569,7 @@ [[package]] name = "fp-evm" version = "3.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "evm", "frame-support", @@ -2583,7 +2583,7 @@ [[package]] name = "fp-evm-mapping" version = "0.1.0" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "frame-support", "sp-core", @@ -2592,7 +2592,7 @@ [[package]] name = "fp-rpc" version = "3.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "ethereum-types", @@ -2609,7 +2609,7 @@ [[package]] name = "fp-self-contained" version = "1.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "frame-support", @@ -2625,7 +2625,7 @@ [[package]] name = "fp-storage" version = "2.0.0" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "parity-scale-codec 3.1.5", ] @@ -5445,7 +5445,7 @@ [[package]] name = "pallet-base-fee" version = "1.0.0" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "fp-evm", "frame-support", @@ -5660,7 +5660,7 @@ [[package]] name = "pallet-ethereum" version = "4.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "ethereum-types", @@ -5689,7 +5689,7 @@ [[package]] name = "pallet-evm" version = "6.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "evm", "fp-evm", @@ -5744,6 +5744,7 @@ "pallet-common", "pallet-evm", "pallet-evm-coder-substrate", + "pallet-evm-transaction-payment", "parity-scale-codec 3.1.5", "scale-info", "sp-core", --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -19,4 +19,4 @@ sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-rpc = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -577,8 +577,8 @@ BlockNumber: Decode + Member + AtLeast32BitUnsigned, AccountId: Decode, C: 'static + ProvideRuntimeApi + HeaderBackend, + CrossAccountId: pallet_evm::account::CrossAccountId, C::Api: AppPromotionRuntimeApi, - CrossAccountId: pallet_evm::account::CrossAccountId, { pass_method!(total_staked(staker: Option) -> String => |v| v.to_string(), app_promotion_api); pass_method!(total_staked_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> => --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -309,13 +309,13 @@ jsonrpsee = { version = "0.14.0", features = ["server", "macros"] } tokio = { version = "1.19.2", features = ["time"] } -fc-rpc-core = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-consensus = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-mapping-sync = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-rpc = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-db = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-rpc = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fc-rpc-core = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-consensus = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-mapping-sync = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-rpc = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-db = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-rpc = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } unique-rpc = { default-features = false, path = "../rpc" } app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false} --- a/node/rpc/Cargo.toml +++ b/node/rpc/Cargo.toml @@ -40,13 +40,13 @@ substrate-frame-rpc-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } tokio = { version = "1.19.2", features = ["macros", "sync"] } -pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-storage = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-rpc-core = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-db = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-mapping-sync = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-storage = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-rpc-core = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-db = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-mapping-sync = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-common = { default-features = false, path = "../../pallets/common" } up-common = { path = "../../primitives/common" } --- a/pallets/app-promotion/Cargo.toml +++ b/pallets/app-promotion/Cargo.toml @@ -52,7 +52,7 @@ pallet-balances ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } pallet-timestamp ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } pallet-randomness-collective-flip ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm ={ default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm ={ default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } sp-std ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } --- a/pallets/common/Cargo.toml +++ b/pallets/common/Cargo.toml @@ -17,12 +17,12 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' } evm-coder = { default-features = false, path = '../../crates/evm-coder' } ethereum = { version = "0.12.0", default-features = false } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } serde = { version = "1.0.130", default-features = false } scale-info = { version = "2.0.1", default-features = false, features = [ --- a/pallets/configuration/Cargo.toml +++ b/pallets/configuration/Cargo.toml @@ -16,7 +16,7 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-arithmetic = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } smallvec = "1.6.1" [features] --- a/pallets/evm-coder-substrate/Cargo.toml +++ b/pallets/evm-coder-substrate/Cargo.toml @@ -12,8 +12,8 @@ sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } ethereum = { version = "0.12.0", default-features = false } evm-coder = { default-features = false, path = "../../crates/evm-coder" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } --- a/pallets/evm-contract-helpers/Cargo.toml +++ b/pallets/evm-contract-helpers/Cargo.toml @@ -19,14 +19,15 @@ sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } # Unique -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } # Locals evm-coder = { default-features = false, path = '../../crates/evm-coder' } pallet-common = { default-features = false, path = '../../pallets/common' } pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' } +pallet-evm-transaction-payment = { default-features = false, path = '../../pallets/evm-transaction-payment' } up-data-structs = { default-features = false, path = '../../primitives/data-structs', features = [ 'serde1', ] } --- a/pallets/evm-contract-helpers/src/eth.rs +++ b/pallets/evm-contract-helpers/src/eth.rs @@ -20,16 +20,17 @@ use evm_coder::{ abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog, }; -use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm}; use pallet_evm::{ ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle, account::CrossAccountId, }; -use sp_core::H160; +use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm}; +use pallet_evm_transaction_payment::CallContext; +use sp_core::{H160, U256}; use up_data_structs::SponsorshipState; use crate::{ - AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT, - Sponsoring, + AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringFeeLimit, + SponsoringRateLimit, SponsoringModeT, Sponsoring, }; use frame_support::traits::Get; use up_sponsorship::SponsorshipHandler; @@ -222,9 +223,11 @@ } /// Get current contract sponsoring rate limit - /// @param contractAddress Contract to get sponsoring mode of + /// @param contractAddress Contract to get sponsoring rate limit of /// @return uint32 Amount of blocks between two sponsored transactions fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result { + self.recorder().consume_sload()?; + Ok(>::get(contract_address) .try_into() .map_err(|_| "rate limit > u32::MAX")?) @@ -250,6 +253,37 @@ Ok(()) } + /// Set contract sponsoring fee limit + /// @dev Sponsoring fee limit - is maximum fee that could be spent by + /// single transaction + /// @param contractAddress Contract to change sponsoring fee limit of + /// @param feeLimit Fee limit + /// @dev Only contract owner can change this setting + fn set_sponsoring_fee_limit( + &mut self, + caller: caller, + contract_address: address, + fee_limit: uint256, + ) -> Result { + self.recorder().consume_sload()?; + self.recorder().consume_sstore()?; + + >::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::)?; + >::set_sponsoring_fee_limit(contract_address, fee_limit.into()) + .map_err(dispatch_to_evm::)?; + Ok(()) + } + + /// Get current contract sponsoring fee limit + /// @param contractAddress Contract to get sponsoring fee limit of + /// @return uint256 Maximum amount of fee that could be spent by single + /// transaction + fn get_sponsoring_fee_limit(&self, contract_address: address) -> Result { + self.recorder().consume_sload()?; + + Ok(get_sponsoring_fee_limit::(contract_address)) + } + /// Is specified user present in contract allow list /// @dev Contract owner always implicitly included /// @param contractAddress Contract to check allowlist of @@ -363,23 +397,26 @@ /// Bridge to pallet-sponsoring pub struct HelpersContractSponsoring(PhantomData<*const T>); -impl SponsorshipHandler)> +impl SponsorshipHandler for HelpersContractSponsoring { - fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec)) -> Option { - let (contract_address, _) = call; - let mode = >::sponsoring_mode(*contract_address); + fn get_sponsor( + who: &T::CrossAccountId, + call_context: &CallContext, + ) -> Option { + let contract_address = call_context.contract_address; + let mode = >::sponsoring_mode(contract_address); if mode == SponsoringModeT::Disabled { return None; } - let sponsor = match >::get_sponsor(*contract_address) { + let sponsor = match >::get_sponsor(contract_address) { Some(sponsor) => sponsor, None => return None, }; if mode == SponsoringModeT::Allowlisted - && !>::allowed(*contract_address, *who.as_eth()) + && !>::allowed(contract_address, *who.as_eth()) { return None; } @@ -394,11 +431,24 @@ } } + let sponsored_fee_limit = get_sponsoring_fee_limit::(contract_address); + + if call_context.max_fee > sponsored_fee_limit { + return None; + } + >::insert(contract_address, who.as_eth(), block_number); Some(sponsor) } } +fn get_sponsoring_fee_limit(contract_address: address) -> uint256 { + >::get(contract_address) + .get(&0xffffffff) + .cloned() + .unwrap_or(U256::MAX) +} + generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true); generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false); --- a/pallets/evm-contract-helpers/src/lib.rs +++ b/pallets/evm-contract-helpers/src/lib.rs @@ -22,15 +22,19 @@ pub use pallet::*; pub use eth::*; use scale_info::TypeInfo; +use frame_support::storage::bounded_btree_map::BoundedBTreeMap; pub mod eth; +/// Maximum number of methods per contract that could have fee limit +pub const MAX_FEE_LIMITED_METHODS: u32 = 5; + #[frame_support::pallet] pub mod pallet { pub use super::*; use crate::eth::ContractHelpersEvents; use frame_support::pallet_prelude::*; use pallet_evm_coder_substrate::DispatchResult; - use sp_core::H160; + use sp_core::{H160, U256}; use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm}; use up_data_structs::SponsorshipState; use evm_coder::ToLog; @@ -57,6 +61,9 @@ /// No pending sponsor for contract. NoPendingSponsor, + + /// Number of methods that sponsored limit is defined for exceeds maximum. + TooManyMethodsHaveSponsoredLimit, } #[pallet::pallet] @@ -118,6 +125,14 @@ /// * **Key2** - sponsored user address. /// * **Value** - last sponsored block number. #[pallet::storage] + pub(super) type SponsoringFeeLimit = StorageMap< + Hasher = Twox128, + Key = H160, + Value = BoundedBTreeMap>, + QueryKind = ValueQuery, + >; + + #[pallet::storage] pub(super) type SponsorBasket = StorageDoubleMap< Hasher1 = Twox128, Key1 = H160, @@ -355,6 +370,16 @@ >::insert(contract, rate_limit); } + /// Set maximum for gas limit of transaction + pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) -> DispatchResult { + >::try_mutate(contract, |limits_map| { + limits_map + .try_insert(0xffffffff, fee_limit) + .map_err(|_| >::TooManyMethodsHaveSponsoredLimit) + })?; + Ok(()) + } + /// Is user added to allowlist, or he is owner of specified contract pub fn allowed(contract: H160, user: H160) -> bool { >::get(&contract, &user) || >::get(&contract) == user --- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol +++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol @@ -32,7 +32,7 @@ } /// @title Magic contract, which allows users to reconfigure other contracts -/// @dev the ERC-165 identifier for this interface is 0xd77fab70 +/// @dev the ERC-165 identifier for this interface is 0x172cb4fb contract ContractHelpers is Dummy, ERC165, ContractHelpersEvents { /// Get user, which deployed specified contract /// @dev May return zero address in case if contract is deployed @@ -171,7 +171,7 @@ } /// Get current contract sponsoring rate limit - /// @param contractAddress Contract to get sponsoring mode of + /// @param contractAddress Contract to get sponsoring rate limit of /// @return uint32 Amount of blocks between two sponsored transactions /// @dev EVM selector for this function is: 0x610cfabd, /// or in textual repr: getSponsoringRateLimit(address) @@ -203,6 +203,40 @@ dummy = 0; } + /// Set contract sponsoring fee limit + /// @dev Sponsoring fee limit - is maximum fee that could be spent by + /// single transaction + /// @param contractAddress Contract to change sponsoring fee limit of + /// @param feeLimit Fee limit + /// @dev Only contract owner can change this setting + /// @dev EVM selector for this function is: 0x03aed665, + /// or in textual repr: setSponsoringFeeLimit(address,uint256) + function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) + public + { + require(false, stub_error); + contractAddress; + feeLimit; + dummy = 0; + } + + /// Get current contract sponsoring fee limit + /// @param contractAddress Contract to get sponsoring fee limit of + /// @return uint256 Maximum amount of fee that could be spent by single + /// transaction + /// @dev EVM selector for this function is: 0xc3fdc9ee, + /// or in textual repr: getSponsoringFeeLimit(address) + function getSponsoringFeeLimit(address contractAddress) + public + view + returns (uint256) + { + require(false, stub_error); + contractAddress; + dummy; + return 0; + } + /// Is specified user present in contract allow list /// @dev Contract owner always implicitly included /// @param contractAddress Contract to check allowlist of --- a/pallets/evm-migration/Cargo.toml +++ b/pallets/evm-migration/Cargo.toml @@ -15,8 +15,8 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-io = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } [dependencies.codec] default-features = false --- a/pallets/evm-transaction-payment/Cargo.toml +++ b/pallets/evm-transaction-payment/Cargo.toml @@ -14,11 +14,11 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-io = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } [dependencies.codec] default-features = false --- a/pallets/evm-transaction-payment/src/lib.rs +++ b/pallets/evm-transaction-payment/src/lib.rs @@ -22,8 +22,8 @@ use fp_evm::WithdrawReason; use frame_support::traits::IsSubType; pub use pallet::*; -use pallet_evm::{EnsureAddressOrigin, account::CrossAccountId}; -use sp_core::H160; +use pallet_evm::{account::CrossAccountId, EnsureAddressOrigin}; +use sp_core::{H160, U256}; use sp_runtime::{TransactionOutcome, DispatchError}; use up_sponsorship::SponsorshipHandler; @@ -33,10 +33,20 @@ use sp_std::vec::Vec; + /// Contains call data + pub struct CallContext { + /// Contract address + pub contract_address: H160, + /// Transaction data + pub input: Vec, + /// Max fee for transaction - gasLimit * gasPrice + pub max_fee: U256, + } + #[pallet::config] pub trait Config: frame_system::Config + pallet_evm::account::Config { /// Loosly-coupled handlers for evm call sponsoring - type EvmSponsorshipHandler: SponsorshipHandler)>; + type EvmSponsorshipHandler: SponsorshipHandler; } #[pallet::pallet] @@ -47,11 +57,20 @@ /// Implements [`fp_evm::TransactionValidityHack`], which provides sponsor address to pallet-evm pub struct TransactionValidityHack(PhantomData<*const T>); impl fp_evm::TransactionValidityHack for TransactionValidityHack { - fn who_pays_fee(origin: H160, reason: &WithdrawReason) -> Option { + fn who_pays_fee( + origin: H160, + max_fee: U256, + reason: &WithdrawReason, + ) -> Option { match reason { WithdrawReason::Call { target, input } => { let origin_sub = T::CrossAccountId::from_eth(origin); - T::EvmSponsorshipHandler::get_sponsor(&origin_sub, &(*target, input.clone())) + let call_context = CallContext { + contract_address: *target, + input: input.clone(), + max_fee, + }; + T::EvmSponsorshipHandler::get_sponsor(&origin_sub, &call_context) } _ => None, } @@ -71,6 +90,8 @@ source, target, input, + gas_limit, + max_fee_per_gas, .. } => { let _ = T::CallOrigin::ensure_address_origin( @@ -79,11 +100,17 @@ ) .ok()?; let who = T::CrossAccountId::from_sub(who.clone()); + let max_fee = max_fee_per_gas.saturating_mul((*gas_limit).into()); + let call_context = CallContext { + contract_address: *target, + input: input.clone(), + max_fee, + }; // Effects from EvmSponsorshipHandler are applied by pallet_evm::runner // TODO: Should we implement simulation mode (test, but do not apply effects) in `up-sponsorship`? let sponsor = frame_support::storage::with_transaction(|| { TransactionOutcome::Rollback(Ok::<_, DispatchError>( - T::EvmSponsorshipHandler::get_sponsor(&who, &(*target, input.clone())), + T::EvmSponsorshipHandler::get_sponsor(&who, &call_context), )) }) // FIXME: it may fail with DispatchError in case of depth limit --- a/pallets/fungible/Cargo.toml +++ b/pallets/fungible/Cargo.toml @@ -23,7 +23,7 @@ pallet-evm-coder-substrate = { default-features = false, path = '../evm-coder-substrate' } ethereum = { version = "0.12.0", default-features = false } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } scale-info = { version = "2.0.1", default-features = false, features = [ "derive", ] } --- a/pallets/nonfungible/Cargo.toml +++ b/pallets/nonfungible/Cargo.toml @@ -16,7 +16,7 @@ sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-common = { default-features = false, path = '../common' } pallet-structure = { default-features = false, path = '../structure' } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } --- a/pallets/proxy-rmrk-core/Cargo.toml +++ b/pallets/proxy-rmrk-core/Cargo.toml @@ -20,7 +20,7 @@ pallet-nonfungible = { default-features = false, path = "../../pallets/nonfungible" } pallet-structure = { default-features = false, path = "../../pallets/structure" } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } rmrk-traits = { default-features = false, path = "../../primitives/rmrk-traits" } scale-info = { version = "2.0.1", default-features = false, features = [ --- a/pallets/proxy-rmrk-equip/Cargo.toml +++ b/pallets/proxy-rmrk-equip/Cargo.toml @@ -19,7 +19,7 @@ pallet-common = { default-features = false, path = '../common' } pallet-nonfungible = { default-features = false, path = "../../pallets/nonfungible" } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } rmrk-traits = { default-features = false, path = "../../primitives/rmrk-traits" } scale-info = { version = "2.0.1", default-features = false, features = [ --- a/pallets/refungible/Cargo.toml +++ b/pallets/refungible/Cargo.toml @@ -16,7 +16,7 @@ sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } 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' } --- a/pallets/structure/Cargo.toml +++ b/pallets/structure/Cargo.toml @@ -16,7 +16,7 @@ "derive", ] } up-data-structs = { path = "../../primitives/data-structs", default-features = false } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } [features] default = ["std"] --- a/pallets/unique/Cargo.toml +++ b/pallets/unique/Cargo.toml @@ -98,7 +98,7 @@ scale-info = { version = "2.0.1", default-features = false, features = [ "derive", ] } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-common = { default-features = false, path = "../common" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' } --- a/pallets/unique/src/eth/mod.rs +++ b/pallets/unique/src/eth/mod.rs @@ -21,25 +21,23 @@ use evm_coder::{execution::*, generate_stubgen, solidity_interface, solidity, weight, types::*}; use frame_support::traits::Get; use pallet_common::{ - CollectionById, CollectionHandle, + CollectionById, dispatch::CollectionDispatch, erc::{ CollectionHelpersEvents, static_property::{key, value as property_value}, }, - Pallet as PalletCommon, }; use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder}; use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult}; -use pallet_evm_coder_substrate::dispatch_to_evm; use up_data_structs::{ CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData, - CollectionMode, PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue, + CollectionMode, PropertyValue, }; use crate::{Config, SelfWeightOf, weights::WeightInfo}; -use sp_std::{vec, vec::Vec}; +use sp_std::vec::Vec; use alloc::format; /// See [`CollectionHelpersCall`] --- a/primitives/app_promotion_rpc/Cargo.toml +++ b/primitives/app_promotion_rpc/Cargo.toml @@ -14,7 +14,7 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-api = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } [features] default = ["std"] --- a/primitives/common/Cargo.toml +++ b/primitives/common/Cargo.toml @@ -48,9 +48,9 @@ [dependencies.fp-rpc] default-features = false git = "https://github.com/uniquenetwork/frontier" -branch = "unique-polkadot-v0.9.27" +branch = "unique-polkadot-v0.9.27-fee-limit" [dependencies.pallet-evm] default-features = false git = "https://github.com/uniquenetwork/frontier" -branch = "unique-polkadot-v0.9.27" +branch = "unique-polkadot-v0.9.27-fee-limit" --- a/primitives/data-structs/Cargo.toml +++ b/primitives/data-structs/Cargo.toml @@ -25,7 +25,7 @@ sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } derivative = { version = "2.2.0", features = ["use_core"] } struct-versioning = { path = "../../crates/struct-versioning" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } rmrk-traits = { default-features = false, path = "../rmrk-traits" } [features] --- a/primitives/rpc/Cargo.toml +++ b/primitives/rpc/Cargo.toml @@ -14,7 +14,7 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-api = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } [features] default = ["std"] --- a/runtime/common/config/sponsoring.rs +++ b/runtime/common/config/sponsoring.rs @@ -14,15 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -use frame_support::parameter_types; use crate::{ runtime_common::{sponsoring::UniqueSponsorshipHandler}, Runtime, }; -use up_common::{types::BlockNumber, constants::*}; +use frame_support::parameter_types; +use sp_core::U256; +use up_common::{constants::*, types::BlockNumber}; parameter_types! { pub const DefaultSponsoringRateLimit: BlockNumber = 1 * DAYS; + pub const DefaultSponsoringFeeLimit: U256 = U256::MAX; } type SponsorshipHandler = ( --- a/runtime/common/ethereum/sponsoring.rs +++ b/runtime/common/ethereum/sponsoring.rs @@ -16,27 +16,30 @@ //! Implements EVM sponsoring logic via TransactionValidityHack +use core::{convert::TryInto, marker::PhantomData}; use evm_coder::{Call, abi::AbiReader}; use pallet_common::{CollectionHandle, eth::map_eth_to_id}; -use sp_core::H160; +use pallet_evm::account::CrossAccountId; +use pallet_evm_transaction_payment::CallContext; +use pallet_nonfungible::{ + Config as NonfungibleConfig, + erc::{ + UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call, + TokenPropertiesCall, + }, +}; +use pallet_fungible::{ + Config as FungibleConfig, + erc::{UniqueFungibleCall, ERC20Call}, +}; +use pallet_refungible::Config as RefungibleConfig; +use pallet_unique::Config as UniqueConfig; use sp_std::prelude::*; +use up_data_structs::{CollectionMode, CreateItemData, CreateNftData, TokenId}; use up_sponsorship::SponsorshipHandler; -use core::marker::PhantomData; -use core::convert::TryInto; -use pallet_evm::account::CrossAccountId; -use up_data_structs::{TokenId, CreateItemData, CreateNftData, CollectionMode}; -use pallet_unique::Config as UniqueConfig; use crate::{Runtime, runtime_common::sponsoring::*}; -use pallet_nonfungible::erc::{ - UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call, TokenPropertiesCall, -}; -use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call}; -use pallet_fungible::Config as FungibleConfig; -use pallet_nonfungible::Config as NonfungibleConfig; -use pallet_refungible::Config as RefungibleConfig; - pub type EvmSponsorshipHandler = ( UniqueEthSponsorshipHandler, pallet_evm_contract_helpers::HelpersContractSponsoring, @@ -44,13 +47,16 @@ pub struct UniqueEthSponsorshipHandler(PhantomData<*const T>); impl - SponsorshipHandler)> for UniqueEthSponsorshipHandler + SponsorshipHandler for UniqueEthSponsorshipHandler { - fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec)) -> Option { - let collection_id = map_eth_to_id(&call.0)?; + fn get_sponsor( + who: &T::CrossAccountId, + call_context: &CallContext, + ) -> Option { + let collection_id = map_eth_to_id(&call_context.contract_address)?; let collection = >::new(collection_id)?; let sponsor = collection.sponsorship.sponsor()?.clone(); - let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?; + let (method_id, mut reader) = AbiReader::new_call(&call_context.input).ok()?; Some(T::CrossAccountId::from_sub(match &collection.mode { CollectionMode::NFT => { let call = >::parse(method_id, &mut reader).ok()??; --- a/runtime/opal/Cargo.toml +++ b/runtime/opal/Cargo.toml @@ -417,7 +417,7 @@ up-rpc = { path = "../../primitives/rpc", default-features = false } app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false} rmrk-rpc = { path = "../../primitives/rmrk-rpc", default-features = false } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-inflation = { path = '../../pallets/inflation', default-features = false } pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false } up-data-structs = { path = '../../primitives/data-structs', default-features = false } @@ -436,13 +436,13 @@ pallet-evm-contract-helpers = { path = '../../pallets/evm-contract-helpers', default-features = false } pallet-evm-transaction-payment = { path = '../../pallets/evm-transaction-payment', default-features = false } pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } ################################################################################ # Build Dependencies --- a/runtime/quartz/Cargo.toml +++ b/runtime/quartz/Cargo.toml @@ -418,7 +418,7 @@ pallet-unique = { path = '../../pallets/unique', default-features = false } up-rpc = { path = "../../primitives/rpc", default-features = false } app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false} -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-inflation = { path = '../../pallets/inflation', default-features = false } pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false } up-data-structs = { path = '../../primitives/data-structs', default-features = false } @@ -437,13 +437,13 @@ pallet-evm-contract-helpers = { path = '../../pallets/evm-contract-helpers', default-features = false } pallet-evm-transaction-payment = { path = '../../pallets/evm-transaction-payment', default-features = false } pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } ################################################################################ # Build Dependencies --- a/runtime/tests/Cargo.toml +++ b/runtime/tests/Cargo.toml @@ -16,7 +16,7 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } @@ -25,8 +25,8 @@ pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-common = { path = '../../pallets/common' } pallet-structure = { path = '../../pallets/structure' } @@ -43,4 +43,4 @@ scale-info = "*" evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } --- a/runtime/unique/Cargo.toml +++ b/runtime/unique/Cargo.toml @@ -430,14 +430,14 @@ pallet-evm-contract-helpers = { path = '../../pallets/evm-contract-helpers', default-features = false } pallet-evm-transaction-payment = { path = '../../pallets/evm-transaction-payment', default-features = false } pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } ################################################################################ # Build Dependencies --- a/tests/src/eth/api/ContractHelpers.sol +++ b/tests/src/eth/api/ContractHelpers.sol @@ -23,7 +23,7 @@ } /// @title Magic contract, which allows users to reconfigure other contracts -/// @dev the ERC-165 identifier for this interface is 0xd77fab70 +/// @dev the ERC-165 identifier for this interface is 0x172cb4fb interface ContractHelpers is Dummy, ERC165, ContractHelpersEvents { /// Get user, which deployed specified contract /// @dev May return zero address in case if contract is deployed @@ -111,7 +111,7 @@ function setSponsoringMode(address contractAddress, uint8 mode) external; /// Get current contract sponsoring rate limit - /// @param contractAddress Contract to get sponsoring mode of + /// @param contractAddress Contract to get sponsoring rate limit of /// @return uint32 Amount of blocks between two sponsored transactions /// @dev EVM selector for this function is: 0x610cfabd, /// or in textual repr: getSponsoringRateLimit(address) @@ -131,6 +131,28 @@ function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) external; + /// Set contract sponsoring fee limit + /// @dev Sponsoring fee limit - is maximum fee that could be spent by + /// single transaction + /// @param contractAddress Contract to change sponsoring fee limit of + /// @param feeLimit Fee limit + /// @dev Only contract owner can change this setting + /// @dev EVM selector for this function is: 0x03aed665, + /// or in textual repr: setSponsoringFeeLimit(address,uint256) + function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) + external; + + /// Get current contract sponsoring fee limit + /// @param contractAddress Contract to get sponsoring fee limit of + /// @return uint256 Maximum amount of fee that could be spent by single + /// transaction + /// @dev EVM selector for this function is: 0xc3fdc9ee, + /// or in textual repr: getSponsoringFeeLimit(address) + function getSponsoringFeeLimit(address contractAddress) + external + view + returns (uint256); + /// Is specified user present in contract allow list /// @dev Contract owner always implicitly included /// @param contractAddress Contract to check allowlist of --- a/tests/src/eth/contractSponsoring.test.ts +++ b/tests/src/eth/contractSponsoring.test.ts @@ -14,8 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . +import * as solc from 'solc'; import {expect} from 'chai'; import {expectSubstrateEventsAtBlock} from '../util/helpers'; +import Web3 from 'web3'; + import { contractHelpers, createEthAccountWithBalance, @@ -26,7 +29,11 @@ createEthAccount, ethBalanceViaSub, normalizeEvents, + CompiledContract, + GAS_ARGS, + subToEth, } from './util/helpers'; +import {submitTransactionAsync} from '../substrate/substrate-api'; describe('Sponsoring EVM contracts', () => { itWeb3('Self sponsored can be set by the address that deployed the contract', async ({api, web3, privateKeyWrapper}) => { @@ -478,3 +485,167 @@ expect(await helpers.methods.getSponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200'); }); }); + +describe('Sponsoring Fee Limit', () => { + + let testContract: CompiledContract; + + function compileTestContract() { + if (!testContract) { + const input = { + language: 'Solidity', + sources: { + ['TestContract.sol']: { + content: + ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.0; + + contract TestContract { + event Result(bool); + + function test(uint32 cycles) public { + uint256 counter = 0; + while(true) { + counter ++; + if (counter > cycles){ + break; + } + } + emit Result(true); + } + } + `, + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['*'], + }, + }, + }, + }; + const json = JSON.parse(solc.compile(JSON.stringify(input))); + const out = json.contracts['TestContract.sol']['TestContract']; + + testContract = { + abi: out.abi, + object: '0x' + out.evm.bytecode.object, + }; + } + return testContract; + } + + async function deployTestContract(web3: Web3, owner: string) { + const compiled = compileTestContract(); + const testContract = new web3.eth.Contract(compiled.abi, undefined, { + data: compiled.object, + from: owner, + ...GAS_ARGS, + }); + return await testContract.deploy({data: compiled.object}).send({from: owner}); + } + + itWeb3('Default fee limit', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const flipper = await deployFlipper(web3, owner); + const helpers = contractHelpers(web3, owner); + expect(await helpers.methods.getSponsoringFeeLimit(flipper.options.address).call()).to.be.equals('115792089237316195423570985008687907853269984665640564039457584007913129639935'); + }); + + itWeb3('Set fee limit', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const flipper = await deployFlipper(web3, owner); + const helpers = contractHelpers(web3, owner); + await helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send(); + expect(await helpers.methods.getSponsoringFeeLimit(flipper.options.address).call()).to.be.equals('100'); + }); + + itWeb3('Negative test - set fee limit by non-owner', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const stranger = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const flipper = await deployFlipper(web3, owner); + const helpers = contractHelpers(web3, owner); + await expect(helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send({from: stranger})).to.be.rejected; + }); + + itWeb3('Negative test - check that eth transactions exceeding fee limit are not executed', async ({api, web3, privateKeyWrapper}) => { + const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const user = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + + const testContract = await deployTestContract(web3, owner); + const helpers = contractHelpers(web3, owner); + + await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner}); + + await helpers.methods.setSponsor(testContract.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor}); + + const gasPrice = BigInt(await web3.eth.getGasPrice()); + + await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send(); + + const originalUserBalance = await web3.eth.getBalance(user); + await testContract.methods.test(100).send({from: user, gas: 2_000_000}); + expect(await web3.eth.getBalance(user)).to.be.equal(originalUserBalance); + + await testContract.methods.test(100).send({from: user, gas: 2_100_000}); + expect(await web3.eth.getBalance(user)).to.not.be.equal(originalUserBalance); + }); + + itWeb3('Negative test - check that evm.call transactions exceeding fee limit are not executed', async ({api, web3, privateKeyWrapper}) => { + const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + + const testContract = await deployTestContract(web3, owner); + const helpers = contractHelpers(web3, owner); + + await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner}); + + await helpers.methods.setSponsor(testContract.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor}); + + const gasPrice = BigInt(await web3.eth.getGasPrice()); + + await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send(); + + const alice = privateKeyWrapper('//Alice'); + const originalAliceBalance = (await api.query.system.account(alice.address)).data.free.toBigInt(); + + await submitTransactionAsync( + alice, + api.tx.evm.call( + subToEth(alice.address), + testContract.options.address, + testContract.methods.test(100).encodeABI(), + Uint8Array.from([]), + 2_000_000n, + gasPrice, + null, + null, + [], + ), + ); + expect((await api.query.system.account(alice.address)).data.free.toBigInt()).to.be.equal(originalAliceBalance); + + await submitTransactionAsync( + alice, + api.tx.evm.call( + subToEth(alice.address), + testContract.options.address, + testContract.methods.test(100).encodeABI(), + Uint8Array.from([]), + 2_100_000n, + gasPrice, + null, + null, + [], + ), + ); + expect((await api.query.system.account(alice.address)).data.free.toBigInt()).to.not.be.equal(originalAliceBalance); + }); +}); --- a/tests/src/eth/util/contractHelpersAbi.json +++ b/tests/src/eth/util/contractHelpersAbi.json @@ -134,6 +134,19 @@ "type": "address" } ], + "name": "getSponsoringFeeLimit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], "name": "getSponsoringRateLimit", "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], "stateMutability": "view", @@ -212,6 +225,20 @@ "name": "contractAddress", "type": "address" }, + { "internalType": "uint256", "name": "feeLimit", "type": "uint256" } + ], + "name": "setSponsoringFeeLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, { "internalType": "uint8", "name": "mode", "type": "uint8" } ], "name": "setSponsoringMode", -- gitstuff