difftreelog
Merge pull request #281 from UniqueNetwork/feature/contract-sponsoring-modes
in: master
Generous contract sponsoring mode
12 files changed
crates/evm-coder/src/abi.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi.rs
+++ b/crates/evm-coder/src/abi.rs
@@ -95,6 +95,10 @@
string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))
}
+ pub fn uint8(&mut self) -> Result<u8> {
+ Ok(self.read_padleft::<1>()?[0])
+ }
+
pub fn uint32(&mut self) -> Result<u32> {
Ok(u32::from_be_bytes(self.read_padleft()?))
}
@@ -243,6 +247,7 @@
};
}
+impl_abi_readable!(u8, uint8);
impl_abi_readable!(u32, uint32);
impl_abi_readable!(u64, uint64);
impl_abi_readable!(u128, uint128);
pallets/evm-contract-helpers/exp.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/exp.rs
+++ /dev/null
@@ -1,1474 +0,0 @@
-#![feature(prelude_import)]
-#[prelude_import]
-use std::prelude::rust_2018::*;
-#[macro_use]
-extern crate std;
-pub use pallet::*;
-pub use eth::*;
-pub mod eth {
- use core::marker::PhantomData;
- use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};
- use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
- use pallet_evm::{ExitReason, ExitRevert, OnCreate, OnMethodCall, PrecompileOutput};
- use sp_core::H160;
- use crate::{
- AllowlistEnabled, Config, Owner, Pallet, SelfSponsoring, SponsorBasket, SponsoringRateLimit,
- };
- use frame_support::traits::Get;
- use up_sponsorship::SponsorshipHandler;
- use sp_std::{convert::TryInto, vec::Vec};
- struct ContractHelpers<T: Config>(SubstrateRecorder<T>);
- impl<T: Config> WithRecorder<T> for ContractHelpers<T> {
- fn recorder(&self) -> &SubstrateRecorder<T> {
- &self.0
- }
- fn into_recorder(self) -> SubstrateRecorder<T> {
- self.0
- }
- }
- impl<T: Config> ContractHelpers<T> {
- fn contract_owner(&self, contract_address: address) -> Result<address> {
- Ok(<Owner<T>>::get(contract_address))
- }
- fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {
- Ok(<SelfSponsoring<T>>::get(contract_address))
- }
- fn toggle_sponsoring(
- &mut self,
- caller: caller,
- contract_address: address,
- enabled: bool,
- ) -> Result<void> {
- <Pallet<T>>::ensure_owner(contract_address, caller)?;
- <Pallet<T>>::toggle_sponsoring(contract_address, enabled);
- Ok(())
- }
- fn set_sponsoring_rate_limit(
- &mut self,
- caller: caller,
- contract_address: address,
- rate_limit: uint32,
- ) -> Result<void> {
- <Pallet<T>>::ensure_owner(contract_address, caller)?;
- <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());
- Ok(())
- }
- fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {
- Ok(<SponsoringRateLimit<T>>::get(contract_address)
- .try_into()
- .map_err(|_| "rate limit > u32::MAX")?)
- }
- fn allowed(&self, contract_address: address, user: address) -> Result<bool> {
- Ok(<Pallet<T>>::allowed(contract_address, user, true))
- }
- fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
- Ok(<AllowlistEnabled<T>>::get(contract_address))
- }
- fn toggle_allowlist(
- &mut self,
- caller: caller,
- contract_address: address,
- enabled: bool,
- ) -> Result<void> {
- <Pallet<T>>::ensure_owner(contract_address, caller)?;
- <Pallet<T>>::toggle_allowlist(contract_address, enabled);
- Ok(())
- }
- fn toggle_allowed(
- &mut self,
- caller: caller,
- contract_address: address,
- user: address,
- allowed: bool,
- ) -> Result<void> {
- <Pallet<T>>::ensure_owner(contract_address, caller)?;
- <Pallet<T>>::toggle_allowed(contract_address, user, allowed);
- Ok(())
- }
- }
- pub enum ContractHelpersCall<T: Config> {
- ERC165Call(::evm_coder::ERC165Call, PhantomData<(T)>),
- ContractOwner {
- contract_address: address,
- },
- SponsoringEnabled {
- contract_address: address,
- },
- ToggleSponsoring {
- contract_address: address,
- enabled: bool,
- },
- SetSponsoringRateLimit {
- contract_address: address,
- rate_limit: uint32,
- },
- GetSponsoringRateLimit {
- contract_address: address,
- },
- Allowed {
- contract_address: address,
- user: address,
- },
- AllowlistEnabled {
- contract_address: address,
- },
- ToggleAllowlist {
- contract_address: address,
- enabled: bool,
- },
- ToggleAllowed {
- contract_address: address,
- user: address,
- allowed: bool,
- },
- }
- #[automatically_derived]
- #[allow(unused_qualifications)]
- impl<T: ::core::fmt::Debug + Config> ::core::fmt::Debug for ContractHelpersCall<T> {
- fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- match (&*self,) {
- (&ContractHelpersCall::ERC165Call(ref __self_0, ref __self_1),) => {
- let debug_trait_builder =
- &mut ::core::fmt::Formatter::debug_tuple(f, "ERC165Call");
- let _ = ::core::fmt::DebugTuple::field(debug_trait_builder, &&(*__self_0));
- let _ = ::core::fmt::DebugTuple::field(debug_trait_builder, &&(*__self_1));
- ::core::fmt::DebugTuple::finish(debug_trait_builder)
- }
- (&ContractHelpersCall::ContractOwner {
- contract_address: ref __self_0,
- },) => {
- let debug_trait_builder =
- &mut ::core::fmt::Formatter::debug_struct(f, "ContractOwner");
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "contract_address",
- &&(*__self_0),
- );
- ::core::fmt::DebugStruct::finish(debug_trait_builder)
- }
- (&ContractHelpersCall::SponsoringEnabled {
- contract_address: ref __self_0,
- },) => {
- let debug_trait_builder =
- &mut ::core::fmt::Formatter::debug_struct(f, "SponsoringEnabled");
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "contract_address",
- &&(*__self_0),
- );
- ::core::fmt::DebugStruct::finish(debug_trait_builder)
- }
- (&ContractHelpersCall::ToggleSponsoring {
- contract_address: ref __self_0,
- enabled: ref __self_1,
- },) => {
- let debug_trait_builder =
- &mut ::core::fmt::Formatter::debug_struct(f, "ToggleSponsoring");
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "contract_address",
- &&(*__self_0),
- );
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "enabled",
- &&(*__self_1),
- );
- ::core::fmt::DebugStruct::finish(debug_trait_builder)
- }
- (&ContractHelpersCall::SetSponsoringRateLimit {
- contract_address: ref __self_0,
- rate_limit: ref __self_1,
- },) => {
- let debug_trait_builder =
- &mut ::core::fmt::Formatter::debug_struct(f, "SetSponsoringRateLimit");
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "contract_address",
- &&(*__self_0),
- );
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "rate_limit",
- &&(*__self_1),
- );
- ::core::fmt::DebugStruct::finish(debug_trait_builder)
- }
- (&ContractHelpersCall::GetSponsoringRateLimit {
- contract_address: ref __self_0,
- },) => {
- let debug_trait_builder =
- &mut ::core::fmt::Formatter::debug_struct(f, "GetSponsoringRateLimit");
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "contract_address",
- &&(*__self_0),
- );
- ::core::fmt::DebugStruct::finish(debug_trait_builder)
- }
- (&ContractHelpersCall::Allowed {
- contract_address: ref __self_0,
- user: ref __self_1,
- },) => {
- let debug_trait_builder =
- &mut ::core::fmt::Formatter::debug_struct(f, "Allowed");
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "contract_address",
- &&(*__self_0),
- );
- let _ =
- ::core::fmt::DebugStruct::field(debug_trait_builder, "user", &&(*__self_1));
- ::core::fmt::DebugStruct::finish(debug_trait_builder)
- }
- (&ContractHelpersCall::AllowlistEnabled {
- contract_address: ref __self_0,
- },) => {
- let debug_trait_builder =
- &mut ::core::fmt::Formatter::debug_struct(f, "AllowlistEnabled");
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "contract_address",
- &&(*__self_0),
- );
- ::core::fmt::DebugStruct::finish(debug_trait_builder)
- }
- (&ContractHelpersCall::ToggleAllowlist {
- contract_address: ref __self_0,
- enabled: ref __self_1,
- },) => {
- let debug_trait_builder =
- &mut ::core::fmt::Formatter::debug_struct(f, "ToggleAllowlist");
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "contract_address",
- &&(*__self_0),
- );
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "enabled",
- &&(*__self_1),
- );
- ::core::fmt::DebugStruct::finish(debug_trait_builder)
- }
- (&ContractHelpersCall::ToggleAllowed {
- contract_address: ref __self_0,
- user: ref __self_1,
- allowed: ref __self_2,
- },) => {
- let debug_trait_builder =
- &mut ::core::fmt::Formatter::debug_struct(f, "ToggleAllowed");
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "contract_address",
- &&(*__self_0),
- );
- let _ =
- ::core::fmt::DebugStruct::field(debug_trait_builder, "user", &&(*__self_1));
- let _ = ::core::fmt::DebugStruct::field(
- debug_trait_builder,
- "allowed",
- &&(*__self_2),
- );
- ::core::fmt::DebugStruct::finish(debug_trait_builder)
- }
- }
- }
- }
- impl<T: Config> ContractHelpersCall<T> {
- #[doc = "contractOwner(address)"]
- const CONTRACT_OWNER: u32 = 1364373836u32;
- #[doc = "sponsoringEnabled(address)"]
- const SPONSORING_ENABLED: u32 = 1613225057u32;
- #[doc = "toggleSponsoring(address,bool)"]
- const TOGGLE_SPONSORING: u32 = 4239158662u32;
- #[doc = "setSponsoringRateLimit(address,uint32)"]
- const SET_SPONSORING_RATE_LIMIT: u32 = 2008467720u32;
- #[doc = "getSponsoringRateLimit(address)"]
- const GET_SPONSORING_RATE_LIMIT: u32 = 1628240573u32;
- #[doc = "allowed(address,address)"]
- const ALLOWED: u32 = 1550156133u32;
- #[doc = "allowlistEnabled(address)"]
- const ALLOWLIST_ENABLED: u32 = 3346198380u32;
- #[doc = "toggleAllowlist(address,bool)"]
- const TOGGLE_ALLOWLIST: u32 = 920527093u32;
- #[doc = "toggleAllowed(address,address,bool)"]
- const TOGGLE_ALLOWED: u32 = 1191627804u32;
- pub const fn interface_id() -> u32 {
- let mut interface_id = 0;
- interface_id ^= Self::CONTRACT_OWNER;
- interface_id ^= Self::SPONSORING_ENABLED;
- interface_id ^= Self::TOGGLE_SPONSORING;
- interface_id ^= Self::SET_SPONSORING_RATE_LIMIT;
- interface_id ^= Self::GET_SPONSORING_RATE_LIMIT;
- interface_id ^= Self::ALLOWED;
- interface_id ^= Self::ALLOWLIST_ENABLED;
- interface_id ^= Self::TOGGLE_ALLOWLIST;
- interface_id ^= Self::TOGGLE_ALLOWED;
- interface_id
- }
- pub fn supports_interface(interface_id: u32) -> bool {
- interface_id != 0xffffff
- && (interface_id == ::evm_coder::ERC165Call::INTERFACE_ID
- || interface_id == Self::interface_id())
- }
- pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {
- use evm_coder::solidity::*;
- use core::fmt::Write;
- let interface = SolidityInterface {
- name: "ContractHelpers",
- selector: Self::interface_id(),
- is: &["Dummy", "ERC165"],
- functions: (
- SolidityFunction {
- docs: &[],
- selector: "contractOwner(address) 5152b14c",
- name: "contractOwner",
- mutability: SolidityMutability::View,
- args: (<NamedArgument<address>>::new("contractAddress"),),
- result: <UnnamedArgument<address>>::default(),
- },
- SolidityFunction {
- docs: &[],
- selector: "sponsoringEnabled(address) 6027dc61",
- name: "sponsoringEnabled",
- mutability: SolidityMutability::View,
- args: (<NamedArgument<address>>::new("contractAddress"),),
- result: <UnnamedArgument<bool>>::default(),
- },
- SolidityFunction {
- docs: &[],
- selector: "toggleSponsoring(address,bool) fcac6d86",
- name: "toggleSponsoring",
- mutability: SolidityMutability::Mutable,
- args: (
- <NamedArgument<address>>::new("contractAddress"),
- <NamedArgument<bool>>::new("enabled"),
- ),
- result: <UnnamedArgument<void>>::default(),
- },
- SolidityFunction {
- docs: &[],
- selector: "setSponsoringRateLimit(address,uint32) 77b6c908",
- name: "setSponsoringRateLimit",
- mutability: SolidityMutability::Mutable,
- args: (
- <NamedArgument<address>>::new("contractAddress"),
- <NamedArgument<uint32>>::new("rateLimit"),
- ),
- result: <UnnamedArgument<void>>::default(),
- },
- SolidityFunction {
- docs: &[],
- selector: "getSponsoringRateLimit(address) 610cfabd",
- name: "getSponsoringRateLimit",
- mutability: SolidityMutability::View,
- args: (<NamedArgument<address>>::new("contractAddress"),),
- result: <UnnamedArgument<uint32>>::default(),
- },
- SolidityFunction {
- docs: &[],
- selector: "allowed(address,address) 5c658165",
- name: "allowed",
- mutability: SolidityMutability::View,
- args: (
- <NamedArgument<address>>::new("contractAddress"),
- <NamedArgument<address>>::new("user"),
- ),
- result: <UnnamedArgument<bool>>::default(),
- },
- SolidityFunction {
- docs: &[],
- selector: "allowlistEnabled(address) c772ef6c",
- name: "allowlistEnabled",
- mutability: SolidityMutability::View,
- args: (<NamedArgument<address>>::new("contractAddress"),),
- result: <UnnamedArgument<bool>>::default(),
- },
- SolidityFunction {
- docs: &[],
- selector: "toggleAllowlist(address,bool) 36de20f5",
- name: "toggleAllowlist",
- mutability: SolidityMutability::Mutable,
- args: (
- <NamedArgument<address>>::new("contractAddress"),
- <NamedArgument<bool>>::new("enabled"),
- ),
- result: <UnnamedArgument<void>>::default(),
- },
- SolidityFunction {
- docs: &[],
- selector: "toggleAllowed(address,address,bool) 4706cc1c",
- name: "toggleAllowed",
- mutability: SolidityMutability::Mutable,
- args: (
- <NamedArgument<address>>::new("contractAddress"),
- <NamedArgument<address>>::new("user"),
- <NamedArgument<bool>>::new("allowed"),
- ),
- result: <UnnamedArgument<void>>::default(),
- },
- ),
- };
- if is_impl {
- tc . collect ("// Common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n" . into ()) ;
- } else {
- tc . collect ("// Common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n" . into ()) ;
- }
- let mut out = string::new();
- if "ContractHelpers".starts_with("Inline") {
- out.push_str("// Inline\n");
- }
- let _ = interface.format(is_impl, &mut out, tc);
- tc.collect(out);
- }
- }
- impl<T: Config> ::evm_coder::Call for ContractHelpersCall<T> {
- fn parse(
- method_id: u32,
- reader: &mut ::evm_coder::abi::AbiReader,
- ) -> ::evm_coder::execution::Result<Option<Self>> {
- use ::evm_coder::abi::AbiRead;
- match method_id {
- ::evm_coder::ERC165Call::INTERFACE_ID => {
- return Ok(
- ::evm_coder::ERC165Call::parse(method_id, reader)?.map(Self::ERC165Call)
- )
- }
- Self::CONTRACT_OWNER => {
- return Ok(Some(Self::ContractOwner {
- contract_address: reader.abi_read()?,
- }))
- }
- Self::SPONSORING_ENABLED => {
- return Ok(Some(Self::SponsoringEnabled {
- contract_address: reader.abi_read()?,
- }))
- }
- Self::TOGGLE_SPONSORING => {
- return Ok(Some(Self::ToggleSponsoring {
- contract_address: reader.abi_read()?,
- enabled: reader.abi_read()?,
- }))
- }
- Self::SET_SPONSORING_RATE_LIMIT => {
- return Ok(Some(Self::SetSponsoringRateLimit {
- contract_address: reader.abi_read()?,
- rate_limit: reader.abi_read()?,
- }))
- }
- Self::GET_SPONSORING_RATE_LIMIT => {
- return Ok(Some(Self::GetSponsoringRateLimit {
- contract_address: reader.abi_read()?,
- }))
- }
- Self::ALLOWED => {
- return Ok(Some(Self::Allowed {
- contract_address: reader.abi_read()?,
- user: reader.abi_read()?,
- }))
- }
- Self::ALLOWLIST_ENABLED => {
- return Ok(Some(Self::AllowlistEnabled {
- contract_address: reader.abi_read()?,
- }))
- }
- Self::TOGGLE_ALLOWLIST => {
- return Ok(Some(Self::ToggleAllowlist {
- contract_address: reader.abi_read()?,
- enabled: reader.abi_read()?,
- }))
- }
- Self::TOGGLE_ALLOWED => {
- return Ok(Some(Self::ToggleAllowed {
- contract_address: reader.abi_read()?,
- user: reader.abi_read()?,
- allowed: reader.abi_read()?,
- }))
- }
- _ => {}
- }
- return Ok(None);
- }
- }
- impl<T: Config> ::evm_coder::Weighted for ContractHelpersCall<T> {
- fn weight(&self) -> ::evm_coder::execution::DispatchInfo {
- type InternalCall = ContractHelpersCall;
- match self {
- InternalCall::ERC165Call(::evm_coder::ERC165Call::SupportsInterface { .. }) => {
- 100u64.into()
- }
- InternalCall::ContractOwner { .. } => ().into(),
- InternalCall::SponsoringEnabled { .. } => ().into(),
- InternalCall::ToggleSponsoring { .. } => ().into(),
- InternalCall::SetSponsoringRateLimit { .. } => ().into(),
- InternalCall::GetSponsoringRateLimit { .. } => ().into(),
- InternalCall::Allowed { .. } => ().into(),
- InternalCall::AllowlistEnabled { .. } => ().into(),
- InternalCall::ToggleAllowlist { .. } => ().into(),
- InternalCall::ToggleAllowed { .. } => ().into(),
- }
- }
- }
- impl<T: Config> ::evm_coder::Callable<ContractHelpersCall<T>> for ContractHelpers<T> {
- #[allow(unreachable_code)]
- fn call(
- &mut self,
- c: Msg<ContractHelpersCall>,
- ) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {
- use ::evm_coder::abi::AbiWrite;
- type InternalCall = ContractHelpersCall;
- match c.call {
- InternalCall::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {
- interface_id,
- }) => {
- let mut writer = ::evm_coder::abi::AbiWriter::default();
- writer.bool(&InternalCall::supports_interface(interface_id));
- return Ok(writer.into());
- }
- _ => {}
- }
- let mut writer = ::evm_coder::abi::AbiWriter::default();
- match c.call {
- InternalCall::ContractOwner { contract_address } => {
- let result = self.contract_owner(contract_address)?;
- (&result).to_result()
- }
- InternalCall::SponsoringEnabled { contract_address } => {
- let result = self.sponsoring_enabled(contract_address)?;
- (&result).to_result()
- }
- InternalCall::ToggleSponsoring {
- contract_address,
- enabled,
- } => {
- let result =
- self.toggle_sponsoring(c.caller.clone(), contract_address, enabled)?;
- (&result).to_result()
- }
- InternalCall::SetSponsoringRateLimit {
- contract_address,
- rate_limit,
- } => {
- let result = self.set_sponsoring_rate_limit(
- c.caller.clone(),
- contract_address,
- rate_limit,
- )?;
- (&result).to_result()
- }
- InternalCall::GetSponsoringRateLimit { contract_address } => {
- let result = self.get_sponsoring_rate_limit(contract_address)?;
- (&result).to_result()
- }
- InternalCall::Allowed {
- contract_address,
- user,
- } => {
- let result = self.allowed(contract_address, user)?;
- (&result).to_result()
- }
- InternalCall::AllowlistEnabled { contract_address } => {
- let result = self.allowlist_enabled(contract_address)?;
- (&result).to_result()
- }
- InternalCall::ToggleAllowlist {
- contract_address,
- enabled,
- } => {
- let result =
- self.toggle_allowlist(c.caller.clone(), contract_address, enabled)?;
- (&result).to_result()
- }
- InternalCall::ToggleAllowed {
- contract_address,
- user,
- allowed,
- } => {
- let result =
- self.toggle_allowed(c.caller.clone(), contract_address, user, allowed)?;
- (&result).to_result()
- }
- _ => ::core::panicking::panic("internal error: entered unreachable code"),
- }
- }
- }
- pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);
- impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T> {
- fn is_reserved(contract: &sp_core::H160) -> bool {
- contract == &T::ContractAddress::get()
- }
- fn is_used(contract: &sp_core::H160) -> bool {
- contract == &T::ContractAddress::get()
- }
- fn call(
- source: &sp_core::H160,
- target: &sp_core::H160,
- gas_left: u64,
- input: &[u8],
- value: sp_core::U256,
- ) -> Option<PrecompileOutput> {
- if !<Pallet<T>>::allowed(*target, *source, true) {
- return Some(PrecompileOutput {
- exit_status: ExitReason::Revert(ExitRevert::Reverted),
- cost: 0,
- output: {
- let mut writer = AbiWriter::new_call(147028384u32);
- writer.string("Target contract is allowlisted");
- writer.finish()
- },
- logs: ::alloc::vec::Vec::new(),
- });
- }
- if target != &T::ContractAddress::get() {
- return None;
- }
- let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(*target, gas_left));
- pallet_evm_coder_substrate::call(*source, helpers, value, input)
- }
- fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {
- (contract == & T :: ContractAddress :: get ()) . then (| | b"`\xe0`@R`&`\x80\x81\x81R\x90a\x04\xf4`\xa09\x80Qa\x00&\x91`\x01\x91` \x90\x91\x01\x90a\x009V[P4\x80\x15a\x003W`\x00\x80\xfd[Pa\x01\rV[\x82\x80Ta\x00E\x90a\x00\xd2V[\x90`\x00R` `\x00 \x90`\x1f\x01` \x90\x04\x81\x01\x92\x82a\x00gW`\x00\x85Ua\x00\xadV[\x82`\x1f\x10a\x00\x80W\x80Q`\xff\x19\x16\x83\x80\x01\x17\x85Ua\x00\xadV[\x82\x80\x01`\x01\x01\x85U\x82\x15a\x00\xadW\x91\x82\x01[\x82\x81\x11\x15a\x00\xadW\x82Q\x82U\x91` \x01\x91\x90`\x01\x01\x90a\x00\x92V[Pa\x00\xb9\x92\x91Pa\x00\xbdV[P\x90V[[\x80\x82\x11\x15a\x00\xb9W`\x00\x81U`\x01\x01a\x00\xbeV[`\x01\x81\x81\x1c\x90\x82\x16\x80a\x00\xe6W`\x7f\x82\x16\x91P[` \x82\x10\x81\x14\x15a\x01\x07WcNH{q`\xe0\x1b`\x00R`\"`\x04R`$`\x00\xfd[P\x91\x90PV[a\x03\xd8\x80a\x01\x1c`\x009`\x00\xf3\xfe`\x80`@R4\x80\x15a\x00\x10W`\x00\x80\xfd[P`\x046\x10a\x00\x9eW`\x005`\xe0\x1c\x80c`\'\xdca\x11a\x00fW\x80c`\'\xdca\x14a\x01\"W\x80ca\x0c\xfa\xbd\x14a\x010W\x80cw\xb6\xc9\x08\x14a\x01SW\x80c\xc7r\xefl\x14a\x01\"W\x80c\xfc\xacm\x86\x14a\x00\xcbW`\x00\x80\xfd[\x80c\x01\xff\xc9\xa7\x14a\x00\xa3W\x80c6\xde \xf5\x14a\x00\xcbW\x80cG\x06\xcc\x1c\x14a\x00\xe0W\x80cQR\xb1L\x14a\x00\xeeW\x80c\\e\x81e\x14a\x01\x14W[`\x00\x80\xfd[a\x00\xb6a\x00\xb16`\x04a\x01\xa2V[a\x01aV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xf3[a\x00\xdea\x00\xd96`\x04a\x01\xffV[a\x01\x87V[\x00[a\x00\xdea\x00\xd96`\x04a\x022V[a\x00\xfca\x00\xb16`\x04a\x02uV[`@Q`\x01`\x01`\xa0\x1b\x03\x90\x91\x16\x81R` \x01a\x00\xc2V[a\x00\xb6a\x00\xb16`\x04a\x02\x90V[a\x00\xb6a\x00\xb16`\x04a\x02uV[a\x01>a\x00\xb16`\x04a\x02uV[`@Qc\xff\xff\xff\xff\x90\x91\x16\x81R` \x01a\x00\xc2V[a\x00\xdea\x00\xd96`\x04a\x02\xbaV[`\x00`\x01`@QbF\x1b\xcd`\xe5\x1b\x81R`\x04\x01a\x01~\x91\x90a\x02\xfaV[`@Q\x80\x91\x03\x90\xfd[`\x01`@QbF\x1b\xcd`\xe5\x1b\x81R`\x04\x01a\x01~\x91\x90a\x02\xfaV[`\x00` \x82\x84\x03\x12\x15a\x01\xb4W`\x00\x80\xfd[\x815`\x01`\x01`\xe0\x1b\x03\x19\x81\x16\x81\x14a\x01\xccW`\x00\x80\xfd[\x93\x92PPPV[\x805`\x01`\x01`\xa0\x1b\x03\x81\x16\x81\x14a\x01\xeaW`\x00\x80\xfd[\x91\x90PV[\x805\x80\x15\x15\x81\x14a\x01\xeaW`\x00\x80\xfd[`\x00\x80`@\x83\x85\x03\x12\x15a\x02\x12W`\x00\x80\xfd[a\x02\x1b\x83a\x01\xd3V[\x91Pa\x02)` \x84\x01a\x01\xefV[\x90P\x92P\x92\x90PV[`\x00\x80`\x00``\x84\x86\x03\x12\x15a\x02GW`\x00\x80\xfd[a\x02P\x84a\x01\xd3V[\x92Pa\x02^` \x85\x01a\x01\xd3V[\x91Pa\x02l`@\x85\x01a\x01\xefV[\x90P\x92P\x92P\x92V[`\x00` \x82\x84\x03\x12\x15a\x02\x87W`\x00\x80\xfd[a\x01\xcc\x82a\x01\xd3V[`\x00\x80`@\x83\x85\x03\x12\x15a\x02\xa3W`\x00\x80\xfd[a\x02\xac\x83a\x01\xd3V[\x91Pa\x02)` \x84\x01a\x01\xd3V[`\x00\x80`@\x83\x85\x03\x12\x15a\x02\xcdW`\x00\x80\xfd[a\x02\xd6\x83a\x01\xd3V[\x91P` \x83\x015c\xff\xff\xff\xff\x81\x16\x81\x14a\x02\xefW`\x00\x80\xfd[\x80\x91PP\x92P\x92\x90PV[`\x00` \x80\x83R`\x00\x84T\x81`\x01\x82\x81\x1c\x91P\x80\x83\x16\x80a\x03\x1cW`\x7f\x83\x16\x92P[\x85\x83\x10\x81\x14\x15a\x03:WcNH{q`\xe0\x1b\x85R`\"`\x04R`$\x85\xfd[\x87\x86\x01\x83\x81R` \x01\x81\x80\x15a\x03WW`\x01\x81\x14a\x03hWa\x03\x93V[`\xff\x19\x86\x16\x82R\x87\x82\x01\x96Pa\x03\x93V[`\x00\x8b\x81R` \x90 `\x00[\x86\x81\x10\x15a\x03\x8dW\x81T\x84\x82\x01R\x90\x85\x01\x90\x89\x01a\x03tV[\x83\x01\x97PP[P\x94\x99\x98PPPPPPPPPV\xfe\xa2dipfsX\"\x12 \xde\xe1\xb0gnP\xa0\xbb\xa7\xaf\xbek+\xe6S6\n\xcd?\x0c+\x81\xebEq\x8c\xe3\xab\xaaC6UdsolcC\x00\x08\t\x003this contract is implemented in native" . to_vec ())
- }
- }
- pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);
- impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {
- fn on_create(owner: H160, contract: H160) {
- <Owner<T>>::insert(contract, owner);
- }
- }
- pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);
- impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {
- fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
- if <SelfSponsoring<T>>::get(&call.0) && <Pallet<T>>::allowed(call.0, *who, false) {
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {
- let rate_limit = <SponsoringRateLimit<T>>::get(&call.0);
- let limit_time = last_tx_block + rate_limit;
- if block_number > limit_time {
- <SponsorBasket<T>>::insert(&call.0, who, block_number);
- return Some(call.0);
- }
- } else {
- <SponsorBasket<T>>::insert(&call.0, who, block_number);
- return Some(call.0);
- }
- }
- None
- }
- }
-}
-#[doc = r"
- The module that hosts all the
- [FRAME](https://docs.substrate.io/v3/runtime/frame)
- types needed to add this pallet to a
- runtime.
- "]
-pub mod pallet {
- use evm_coder::execution::Result;
- use frame_support::pallet_prelude::*;
- use sp_core::H160;
- #[doc = r"
- Configuration trait of this pallet.
-
- Implement this type for a runtime in order to customize this pallet.
- "]
- pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config {
- type ContractAddress: Get<H160>;
- type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;
- }
- #[scale_info(skip_type_params(T), capture_docs = "always")]
- #[doc = r"
- Custom [dispatch errors](https://docs.substrate.io/v3/runtime/events-and-errors)
- of this pallet.
- "]
- pub enum Error<T> {
- #[doc(hidden)]
- #[codec(skip)]
- __Ignore(
- frame_support::sp_std::marker::PhantomData<(T)>,
- frame_support::Never,
- ),
- #[doc = " This method is only executable by owner"]
- NoPermission,
- }
- #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
- const _: () = {
- impl<T> ::scale_info::TypeInfo for Error<T>
- where
- frame_support::sp_std::marker::PhantomData<(T)>: ::scale_info::TypeInfo + 'static,
- T: 'static,
- {
- type Identity = Self;
- fn type_info() -> ::scale_info::Type {
- :: scale_info :: Type :: builder () . path (:: scale_info :: Path :: new ("Error" , "pallet_evm_contract_helpers::pallet")) . type_params (< [_] > :: into_vec (box [:: scale_info :: TypeParameter :: new ("T" , :: core :: option :: Option :: None)])) . docs_always (& ["\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/v3/runtime/events-and-errors)\n\t\t\tof this pallet.\n\t\t\t"]) . variant (:: scale_info :: build :: Variants :: new () . variant ("NoPermission" , | v | v . index (0usize as :: core :: primitive :: u8) . docs_always (& ["This method is only executable by owner"])))
- }
- };
- };
- #[doc = r"
- The [pallet](https://docs.substrate.io/v3/runtime/frame#pallets) implementing
- the on-chain logic.
- "]
- pub struct Pallet<T>(frame_support::sp_std::marker::PhantomData<(T)>);
- const _: () = {
- impl<T> core::clone::Clone for Pallet<T> {
- fn clone(&self) -> Self {
- Self(core::clone::Clone::clone(&self.0))
- }
- }
- };
- const _: () = {
- impl<T> core::cmp::Eq for Pallet<T> {}
- };
- const _: () = {
- impl<T> core::cmp::PartialEq for Pallet<T> {
- fn eq(&self, other: &Self) -> bool {
- true && self.0 == other.0
- }
- }
- };
- const _: () = {
- impl<T> core::fmt::Debug for Pallet<T> {
- fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
- fmt.debug_tuple("Pallet").field(&self.0).finish()
- }
- }
- };
- #[allow(type_alias_bounds)]
- pub(super) type Owner<T: Config> = StorageMap<
- _GeneratedPrefixForStorageOwner<T>,
- Twox128,
- H160,
- H160,
- ValueQuery,
- frame_support::traits::GetDefault,
- frame_support::traits::GetDefault,
- >;
- #[allow(type_alias_bounds)]
- pub(super) type SelfSponsoring<T: Config> = StorageMap<
- _GeneratedPrefixForStorageSelfSponsoring<T>,
- Twox128,
- H160,
- bool,
- ValueQuery,
- frame_support::traits::GetDefault,
- frame_support::traits::GetDefault,
- >;
- #[allow(type_alias_bounds)]
- pub(super) type SponsoringRateLimit<T: Config> = StorageMap<
- _GeneratedPrefixForStorageSponsoringRateLimit<T>,
- Twox128,
- H160,
- T::BlockNumber,
- ValueQuery,
- T::DefaultSponsoringRateLimit,
- frame_support::traits::GetDefault,
- >;
- #[allow(type_alias_bounds)]
- pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<
- _GeneratedPrefixForStorageSponsorBasket<T>,
- Twox128,
- H160,
- Twox128,
- H160,
- T::BlockNumber,
- OptionQuery,
- frame_support::traits::GetDefault,
- frame_support::traits::GetDefault,
- >;
- #[allow(type_alias_bounds)]
- pub(super) type AllowlistEnabled<T: Config> = StorageMap<
- _GeneratedPrefixForStorageAllowlistEnabled<T>,
- Twox128,
- H160,
- bool,
- ValueQuery,
- frame_support::traits::GetDefault,
- frame_support::traits::GetDefault,
- >;
- #[allow(type_alias_bounds)]
- pub(super) type Allowlist<T: Config> = StorageDoubleMap<
- _GeneratedPrefixForStorageAllowlist<T>,
- Twox128,
- H160,
- Twox128,
- H160,
- bool,
- ValueQuery,
- frame_support::traits::GetDefault,
- frame_support::traits::GetDefault,
- >;
- impl<T: Config> Pallet<T> {
- pub fn toggle_sponsoring(contract: H160, enabled: bool) {
- <SelfSponsoring<T>>::insert(contract, enabled);
- }
- pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {
- <SponsoringRateLimit<T>>::insert(contract, rate_limit);
- }
- #[doc = " Default is returned if allowlist is disabled"]
- pub fn allowed(contract: H160, user: H160, default: bool) -> bool {
- if !<AllowlistEnabled<T>>::get(contract) {
- return default;
- }
- <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user
- }
- pub fn toggle_allowlist(contract: H160, enabled: bool) {
- <AllowlistEnabled<T>>::insert(contract, enabled)
- }
- pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {
- <Allowlist<T>>::insert(contract, user, allowed);
- }
- pub fn ensure_owner(contract: H160, user: H160) -> Result<()> {
- {
- if !(<Owner<T>>::get(&contract) == user) {
- {
- return Err("no permission".into());
- };
- }
- };
- Ok(())
- }
- }
- impl<T: Config> Pallet<T> {
- #[doc(hidden)]
- pub fn pallet_constants_metadata(
- ) -> frame_support::sp_std::vec::Vec<frame_support::metadata::PalletConstantMetadata>
- {
- ::alloc::vec::Vec::new()
- }
- }
- impl<T: Config> Pallet<T> {
- pub fn error_metadata() -> Option<frame_support::metadata::PalletErrorMetadata> {
- Some(frame_support::metadata::PalletErrorMetadata {
- ty: frame_support::scale_info::meta_type::<Error<T>>(),
- })
- }
- }
- #[doc = r" Type alias to `Pallet`, to be used by `construct_runtime`."]
- #[doc = r""]
- #[doc = r" Generated by `pallet` attribute macro."]
- #[deprecated(note = "use `Pallet` instead")]
- #[allow(dead_code)]
- pub type Module<T> = Pallet<T>;
- impl<T: Config> frame_support::traits::GetStorageVersion for Pallet<T> {
- fn current_storage_version() -> frame_support::traits::StorageVersion {
- frame_support::traits::StorageVersion::default()
- }
- fn on_chain_storage_version() -> frame_support::traits::StorageVersion {
- frame_support::traits::StorageVersion::get::<Self>()
- }
- }
- impl<T: Config> frame_support::traits::OnGenesis for Pallet<T> {
- fn on_genesis() {
- let storage_version = frame_support::traits::StorageVersion::default();
- storage_version.put::<Self>();
- }
- }
- impl<T: Config> frame_support::traits::PalletInfoAccess for Pallet<T> {
- fn index() -> usize {
- <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::index::<
- Self,
- >()
- .expect(
- "Pallet is part of the runtime because pallet `Config` trait is \
- implemented by the runtime",
- )
- }
- fn name() -> &'static str {
- <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
- Self,
- >()
- .expect(
- "Pallet is part of the runtime because pallet `Config` trait is \
- implemented by the runtime",
- )
- }
- fn module_name() -> &'static str {
- < < T as frame_system :: Config > :: PalletInfo as frame_support :: traits :: PalletInfo > :: module_name :: < Self > () . expect ("Pallet is part of the runtime because pallet `Config` trait is \
- implemented by the runtime")
- }
- fn crate_version() -> frame_support::traits::CrateVersion {
- frame_support::traits::CrateVersion {
- major: 0u16,
- minor: 1u8,
- patch: 0u8,
- }
- }
- }
- impl<T: Config> frame_support::traits::StorageInfoTrait for Pallet<T> {
- fn storage_info() -> frame_support::sp_std::vec::Vec<frame_support::traits::StorageInfo> {
- #[allow(unused_mut)]
- let mut res = ::alloc::vec::Vec::new();
- {
- let mut storage_info = < Owner < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
- res.append(&mut storage_info);
- }
- {
- let mut storage_info = < SelfSponsoring < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
- res.append(&mut storage_info);
- }
- {
- let mut storage_info = < SponsoringRateLimit < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
- res.append(&mut storage_info);
- }
- {
- let mut storage_info = < SponsorBasket < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
- res.append(&mut storage_info);
- }
- {
- let mut storage_info = < AllowlistEnabled < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
- res.append(&mut storage_info);
- }
- {
- let mut storage_info = < Allowlist < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
- res.append(&mut storage_info);
- }
- res
- }
- }
- #[doc(hidden)]
- pub mod __substrate_call_check {
- #[doc(hidden)]
- pub use __is_call_part_defined_0 as is_call_part_defined;
- }
- #[doc = r"Contains one variant per dispatchable that can be called by an extrinsic."]
- #[codec(encode_bound())]
- #[codec(decode_bound())]
- #[scale_info(skip_type_params(T), capture_docs = "always")]
- #[allow(non_camel_case_types)]
- pub enum Call<T: Config> {
- #[doc(hidden)]
- #[codec(skip)]
- __Ignore(
- frame_support::sp_std::marker::PhantomData<(T,)>,
- frame_support::Never,
- ),
- }
- const _: () = {
- impl<T: Config> core::fmt::Debug for Call<T> {
- fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
- match *self {
- Self::__Ignore(ref _0, ref _1) => fmt
- .debug_tuple("Call::__Ignore")
- .field(&_0)
- .field(&_1)
- .finish(),
- }
- }
- }
- };
- const _: () = {
- impl<T: Config> core::clone::Clone for Call<T> {
- fn clone(&self) -> Self {
- match self {
- Self::__Ignore(ref _0, ref _1) => {
- Self::__Ignore(core::clone::Clone::clone(_0), core::clone::Clone::clone(_1))
- }
- }
- }
- }
- };
- const _: () = {
- impl<T: Config> core::cmp::Eq for Call<T> {}
- };
- const _: () = {
- impl<T: Config> core::cmp::PartialEq for Call<T> {
- fn eq(&self, other: &Self) -> bool {
- match (self, other) {
- (Self::__Ignore(_0, _1), Self::__Ignore(_0_other, _1_other)) => {
- true && _0 == _0_other && _1 == _1_other
- }
- }
- }
- }
- };
- const _: () = {
- #[allow(non_camel_case_types)]
- impl<T: Config> ::codec::Encode for Call<T> {}
- impl<T: Config> ::codec::EncodeLike for Call<T> {}
- };
- const _: () = {
- #[allow(non_camel_case_types)]
- impl<T: Config> ::codec::Decode for Call<T> {
- fn decode<__CodecInputEdqy: ::codec::Input>(
- __codec_input_edqy: &mut __CodecInputEdqy,
- ) -> ::core::result::Result<Self, ::codec::Error> {
- match __codec_input_edqy
- .read_byte()
- .map_err(|e| e.chain("Could not decode `Call`, failed to read variant byte"))?
- {
- _ => ::core::result::Result::Err(<_ as ::core::convert::Into<_>>::into(
- "Could not decode `Call`, variant doesn\'t exist",
- )),
- }
- }
- }
- };
- #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
- const _: () = {
- impl<T: Config> ::scale_info::TypeInfo for Call<T>
- where
- frame_support::sp_std::marker::PhantomData<(T,)>: ::scale_info::TypeInfo + 'static,
- T: Config + 'static,
- {
- type Identity = Self;
- fn type_info() -> ::scale_info::Type {
- ::scale_info::Type::builder()
- .path(::scale_info::Path::new(
- "Call",
- "pallet_evm_contract_helpers::pallet",
- ))
- .type_params(<[_]>::into_vec(box [::scale_info::TypeParameter::new(
- "T",
- ::core::option::Option::None,
- )]))
- .docs_always(&[
- "Contains one variant per dispatchable that can be called by an extrinsic.",
- ])
- .variant(::scale_info::build::Variants::new())
- }
- };
- };
- impl<T: Config> Call<T> {}
- impl<T: Config> frame_support::dispatch::GetDispatchInfo for Call<T> {
- fn get_dispatch_info(&self) -> frame_support::dispatch::DispatchInfo {
- match *self {
- Self::__Ignore(_, _) => {
- ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
- &["internal error: entered unreachable code: "],
- &match (&"__Ignore cannot be used",) {
- (arg0,) => [::core::fmt::ArgumentV1::new(
- arg0,
- ::core::fmt::Display::fmt,
- )],
- },
- ))
- }
- }
- }
- }
- impl<T: Config> frame_support::dispatch::GetCallName for Call<T> {
- fn get_call_name(&self) -> &'static str {
- match *self {
- Self::__Ignore(_, _) => {
- ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
- &["internal error: entered unreachable code: "],
- &match (&"__PhantomItem cannot be used.",) {
- (arg0,) => [::core::fmt::ArgumentV1::new(
- arg0,
- ::core::fmt::Display::fmt,
- )],
- },
- ))
- }
- }
- }
- fn get_call_names() -> &'static [&'static str] {
- &[]
- }
- }
- impl<T: Config> frame_support::traits::UnfilteredDispatchable for Call<T> {
- type Origin = frame_system::pallet_prelude::OriginFor<T>;
- fn dispatch_bypass_filter(
- self,
- origin: Self::Origin,
- ) -> frame_support::dispatch::DispatchResultWithPostInfo {
- match self {
- Self::__Ignore(_, _) => {
- let _ = origin;
- {
- {
- ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
- &["internal error: entered unreachable code: "],
- &match (&"__PhantomItem cannot be used.",) {
- (arg0,) => [::core::fmt::ArgumentV1::new(
- arg0,
- ::core::fmt::Display::fmt,
- )],
- },
- ))
- }
- };
- }
- }
- }
- }
- impl<T: Config> frame_support::dispatch::Callable<T> for Pallet<T> {
- type Call = Call<T>;
- }
- impl<T: Config> Pallet<T> {
- #[doc(hidden)]
- pub fn call_functions() -> frame_support::metadata::PalletCallMetadata {
- frame_support::scale_info::meta_type::<Call<T>>().into()
- }
- }
- impl<T: Config> frame_support::sp_std::fmt::Debug for Error<T> {
- fn fmt(
- &self,
- f: &mut frame_support::sp_std::fmt::Formatter<'_>,
- ) -> frame_support::sp_std::fmt::Result {
- f.write_str(self.as_str())
- }
- }
- impl<T: Config> Error<T> {
- pub fn as_u8(&self) -> u8 {
- match &self {
- Self::__Ignore(_, _) => {
- ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
- &["internal error: entered unreachable code: "],
- &match (&"`__Ignore` can never be constructed",) {
- (arg0,) => [::core::fmt::ArgumentV1::new(
- arg0,
- ::core::fmt::Display::fmt,
- )],
- },
- ))
- }
- Self::NoPermission => 0usize as u8,
- }
- }
- pub fn as_str(&self) -> &'static str {
- match &self {
- Self::__Ignore(_, _) => {
- ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
- &["internal error: entered unreachable code: "],
- &match (&"`__Ignore` can never be constructed",) {
- (arg0,) => [::core::fmt::ArgumentV1::new(
- arg0,
- ::core::fmt::Display::fmt,
- )],
- },
- ))
- }
- Self::NoPermission => "NoPermission",
- }
- }
- }
- impl<T: Config> From<Error<T>> for &'static str {
- fn from(err: Error<T>) -> &'static str {
- err.as_str()
- }
- }
- impl<T: Config> From<Error<T>> for frame_support::sp_runtime::DispatchError {
- fn from(err: Error<T>) -> Self {
- let index = < < T as frame_system :: Config > :: PalletInfo as frame_support :: traits :: PalletInfo > :: index :: < Pallet < T > > () . expect ("Every active module has an index in the runtime; qed") as u8 ;
- frame_support::sp_runtime::DispatchError::Module {
- index,
- error: err.as_u8(),
- message: Some(err.as_str()),
- }
- }
- }
- #[doc(hidden)]
- pub mod __substrate_event_check {
- #[doc(hidden)]
- pub use __is_event_part_defined_1 as is_event_part_defined;
- }
- impl<T: Config> Pallet<T> {
- #[doc(hidden)]
- pub fn storage_metadata() -> frame_support::metadata::PalletStorageMetadata {
- frame_support :: metadata :: PalletStorageMetadata { prefix : < < T as frame_system :: Config > :: PalletInfo as frame_support :: traits :: PalletInfo > :: name :: < Pallet < T > > () . expect ("Every active pallet has a name in the runtime; qed") , entries : { # [allow (unused_mut)] let mut entries = :: alloc :: vec :: Vec :: new () ; { < Owner < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } { < SelfSponsoring < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } { < SponsoringRateLimit < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } { < SponsorBasket < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } { < AllowlistEnabled < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } { < Allowlist < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } entries } , }
- }
- }
- pub(super) struct _GeneratedPrefixForStorageOwner<T>(core::marker::PhantomData<(T,)>);
- impl<T: Config> frame_support::traits::StorageInstance for _GeneratedPrefixForStorageOwner<T> {
- fn pallet_prefix() -> &'static str {
- <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
- Pallet<T>,
- >()
- .expect("Every active pallet has a name in the runtime; qed")
- }
- const STORAGE_PREFIX: &'static str = "Owner";
- }
- pub(super) struct _GeneratedPrefixForStorageSelfSponsoring<T>(core::marker::PhantomData<(T,)>);
- impl<T: Config> frame_support::traits::StorageInstance
- for _GeneratedPrefixForStorageSelfSponsoring<T>
- {
- fn pallet_prefix() -> &'static str {
- <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
- Pallet<T>,
- >()
- .expect("Every active pallet has a name in the runtime; qed")
- }
- const STORAGE_PREFIX: &'static str = "SelfSponsoring";
- }
- pub(super) struct _GeneratedPrefixForStorageSponsoringRateLimit<T>(
- core::marker::PhantomData<(T,)>,
- );
- impl<T: Config> frame_support::traits::StorageInstance
- for _GeneratedPrefixForStorageSponsoringRateLimit<T>
- {
- fn pallet_prefix() -> &'static str {
- <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
- Pallet<T>,
- >()
- .expect("Every active pallet has a name in the runtime; qed")
- }
- const STORAGE_PREFIX: &'static str = "SponsoringRateLimit";
- }
- pub(super) struct _GeneratedPrefixForStorageSponsorBasket<T>(core::marker::PhantomData<(T,)>);
- impl<T: Config> frame_support::traits::StorageInstance
- for _GeneratedPrefixForStorageSponsorBasket<T>
- {
- fn pallet_prefix() -> &'static str {
- <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
- Pallet<T>,
- >()
- .expect("Every active pallet has a name in the runtime; qed")
- }
- const STORAGE_PREFIX: &'static str = "SponsorBasket";
- }
- pub(super) struct _GeneratedPrefixForStorageAllowlistEnabled<T>(
- core::marker::PhantomData<(T,)>,
- );
- impl<T: Config> frame_support::traits::StorageInstance
- for _GeneratedPrefixForStorageAllowlistEnabled<T>
- {
- fn pallet_prefix() -> &'static str {
- <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
- Pallet<T>,
- >()
- .expect("Every active pallet has a name in the runtime; qed")
- }
- const STORAGE_PREFIX: &'static str = "AllowlistEnabled";
- }
- pub(super) struct _GeneratedPrefixForStorageAllowlist<T>(core::marker::PhantomData<(T,)>);
- impl<T: Config> frame_support::traits::StorageInstance for _GeneratedPrefixForStorageAllowlist<T> {
- fn pallet_prefix() -> &'static str {
- <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
- Pallet<T>,
- >()
- .expect("Every active pallet has a name in the runtime; qed")
- }
- const STORAGE_PREFIX: &'static str = "Allowlist";
- }
- #[doc(hidden)]
- pub mod __substrate_inherent_check {
- #[doc(hidden)]
- pub use __is_inherent_part_defined_2 as is_inherent_part_defined;
- }
- #[doc = r" Hidden instance generated to be internally used when module is used without"]
- #[doc = r" instance."]
- #[doc(hidden)]
- pub type __InherentHiddenInstance = ();
- pub(super) trait Store {
- type Owner;
- type SelfSponsoring;
- type SponsoringRateLimit;
- type SponsorBasket;
- type AllowlistEnabled;
- type Allowlist;
- }
- impl<T: Config> Store for Pallet<T> {
- type Owner = Owner<T>;
- type SelfSponsoring = SelfSponsoring<T>;
- type SponsoringRateLimit = SponsoringRateLimit<T>;
- type SponsorBasket = SponsorBasket<T>;
- type AllowlistEnabled = AllowlistEnabled<T>;
- type Allowlist = Allowlist<T>;
- }
- impl<T: Config> frame_support::traits::Hooks<<T as frame_system::Config>::BlockNumber>
- for Pallet<T>
- {
- }
- impl<T: Config> frame_support::traits::OnFinalize<<T as frame_system::Config>::BlockNumber>
- for Pallet<T>
- {
- fn on_finalize(n: <T as frame_system::Config>::BlockNumber) {
- let __within_span__ = {
- use ::tracing::__macro_support::Callsite as _;
- static CALLSITE: ::tracing::__macro_support::MacroCallsite = {
- use ::tracing::__macro_support::MacroCallsite;
- static META: ::tracing::Metadata<'static> = {
- ::tracing_core::metadata::Metadata::new(
- "on_finalize",
- "pallet_evm_contract_helpers::pallet",
- ::tracing::Level::TRACE,
- Some("pallets/evm-contract-helpers/src/lib.rs"),
- Some(7u32),
- Some("pallet_evm_contract_helpers::pallet"),
- ::tracing_core::field::FieldSet::new(
- &[],
- ::tracing_core::callsite::Identifier(&CALLSITE),
- ),
- ::tracing::metadata::Kind::SPAN,
- )
- };
- MacroCallsite::new(&META)
- };
- let mut interest = ::tracing::subscriber::Interest::never();
- if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
- && ::tracing::Level::TRACE <= ::tracing::level_filters::LevelFilter::current()
- && {
- interest = CALLSITE.interest();
- !interest.is_never()
- }
- && CALLSITE.is_enabled(interest)
- {
- let meta = CALLSITE.metadata();
- ::tracing::Span::new(meta, &{ meta.fields().value_set(&[]) })
- } else {
- let span = CALLSITE.disabled_span();
- {};
- span
- }
- };
- let __tracing_guard__ = __within_span__.enter();
- < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: on_finalize (n)
- }
- }
- impl<T: Config> frame_support::traits::OnIdle<<T as frame_system::Config>::BlockNumber>
- for Pallet<T>
- {
- fn on_idle(
- n: <T as frame_system::Config>::BlockNumber,
- remaining_weight: frame_support::weights::Weight,
- ) -> frame_support::weights::Weight {
- < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: on_idle (n , remaining_weight)
- }
- }
- impl<T: Config> frame_support::traits::OnInitialize<<T as frame_system::Config>::BlockNumber>
- for Pallet<T>
- {
- fn on_initialize(
- n: <T as frame_system::Config>::BlockNumber,
- ) -> frame_support::weights::Weight {
- let __within_span__ = {
- use ::tracing::__macro_support::Callsite as _;
- static CALLSITE: ::tracing::__macro_support::MacroCallsite = {
- use ::tracing::__macro_support::MacroCallsite;
- static META: ::tracing::Metadata<'static> = {
- ::tracing_core::metadata::Metadata::new(
- "on_initialize",
- "pallet_evm_contract_helpers::pallet",
- ::tracing::Level::TRACE,
- Some("pallets/evm-contract-helpers/src/lib.rs"),
- Some(7u32),
- Some("pallet_evm_contract_helpers::pallet"),
- ::tracing_core::field::FieldSet::new(
- &[],
- ::tracing_core::callsite::Identifier(&CALLSITE),
- ),
- ::tracing::metadata::Kind::SPAN,
- )
- };
- MacroCallsite::new(&META)
- };
- let mut interest = ::tracing::subscriber::Interest::never();
- if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
- && ::tracing::Level::TRACE <= ::tracing::level_filters::LevelFilter::current()
- && {
- interest = CALLSITE.interest();
- !interest.is_never()
- }
- && CALLSITE.is_enabled(interest)
- {
- let meta = CALLSITE.metadata();
- ::tracing::Span::new(meta, &{ meta.fields().value_set(&[]) })
- } else {
- let span = CALLSITE.disabled_span();
- {};
- span
- }
- };
- let __tracing_guard__ = __within_span__.enter();
- < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: on_initialize (n)
- }
- }
- impl<T: Config> frame_support::traits::OnRuntimeUpgrade for Pallet<T> {
- fn on_runtime_upgrade() -> frame_support::weights::Weight {
- let __within_span__ = {
- use ::tracing::__macro_support::Callsite as _;
- static CALLSITE: ::tracing::__macro_support::MacroCallsite = {
- use ::tracing::__macro_support::MacroCallsite;
- static META: ::tracing::Metadata<'static> = {
- ::tracing_core::metadata::Metadata::new(
- "on_runtime_update",
- "pallet_evm_contract_helpers::pallet",
- ::tracing::Level::TRACE,
- Some("pallets/evm-contract-helpers/src/lib.rs"),
- Some(7u32),
- Some("pallet_evm_contract_helpers::pallet"),
- ::tracing_core::field::FieldSet::new(
- &[],
- ::tracing_core::callsite::Identifier(&CALLSITE),
- ),
- ::tracing::metadata::Kind::SPAN,
- )
- };
- MacroCallsite::new(&META)
- };
- let mut interest = ::tracing::subscriber::Interest::never();
- if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
- && ::tracing::Level::TRACE <= ::tracing::level_filters::LevelFilter::current()
- && {
- interest = CALLSITE.interest();
- !interest.is_never()
- }
- && CALLSITE.is_enabled(interest)
- {
- let meta = CALLSITE.metadata();
- ::tracing::Span::new(meta, &{ meta.fields().value_set(&[]) })
- } else {
- let span = CALLSITE.disabled_span();
- {};
- span
- }
- };
- let __tracing_guard__ = __within_span__.enter();
- let pallet_name = < < T as frame_system :: Config > :: PalletInfo as frame_support :: traits :: PalletInfo > :: name :: < Self > () . unwrap_or ("<unknown pallet name>") ;
- {
- let lvl = ::log::Level::Info;
- if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() {
- ::log::__private_api_log(
- ::core::fmt::Arguments::new_v1(
- &["\u{2705} no migration for "],
- &match (&pallet_name,) {
- (arg0,) => [::core::fmt::ArgumentV1::new(
- arg0,
- ::core::fmt::Display::fmt,
- )],
- },
- ),
- lvl,
- &(
- frame_support::LOG_TARGET,
- "pallet_evm_contract_helpers::pallet",
- "pallets/evm-contract-helpers/src/lib.rs",
- 7u32,
- ),
- );
- }
- };
- < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: on_runtime_upgrade ()
- }
- }
- impl<T: Config> frame_support::traits::OffchainWorker<<T as frame_system::Config>::BlockNumber>
- for Pallet<T>
- {
- fn offchain_worker(n: <T as frame_system::Config>::BlockNumber) {
- < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: offchain_worker (n)
- }
- }
- impl<T: Config> frame_support::traits::IntegrityTest for Pallet<T> {
- fn integrity_test() {
- < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: integrity_test ()
- }
- }
- #[doc(hidden)]
- pub mod __substrate_genesis_config_check {
- #[doc(hidden)]
- pub use __is_genesis_config_defined_3 as is_genesis_config_defined;
- #[doc(hidden)]
- pub use __is_std_enabled_for_genesis_3 as is_std_enabled_for_genesis;
- }
- #[doc(hidden)]
- pub mod __substrate_origin_check {
- #[doc(hidden)]
- pub use __is_origin_part_defined_4 as is_origin_part_defined;
- }
- #[doc(hidden)]
- pub mod __substrate_validate_unsigned_check {
- #[doc(hidden)]
- pub use __is_validate_unsigned_part_defined_5 as is_validate_unsigned_part_defined;
- }
-}
pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -4,7 +4,7 @@
use pallet_evm::{ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure};
use sp_core::H160;
use crate::{
- AllowlistEnabled, Config, Owner, Pallet, SelfSponsoring, SponsorBasket, SponsoringRateLimit,
+ AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT,
};
use frame_support::traits::Get;
use up_sponsorship::SponsorshipHandler;
@@ -28,9 +28,10 @@
}
fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {
- Ok(<SelfSponsoring<T>>::get(contract_address))
+ Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)
}
+ /// Deprecated
fn toggle_sponsoring(
&mut self,
caller: caller,
@@ -42,6 +43,22 @@
Ok(())
}
+ fn set_sponsoring_mode(
+ &mut self,
+ caller: caller,
+ contract_address: address,
+ mode: uint8,
+ ) -> Result<void> {
+ <Pallet<T>>::ensure_owner(contract_address, caller)?;
+ let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;
+ <Pallet<T>>::set_sponsoring_mode(contract_address, mode);
+ Ok(())
+ }
+
+ fn sponsoring_mode(&self, contract_address: address) -> Result<uint8> {
+ Ok(<Pallet<T>>::sponsoring_mode(contract_address).to_eth())
+ }
+
fn set_sponsoring_rate_limit(
&mut self,
caller: caller,
@@ -146,10 +163,11 @@
pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);
impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {
fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
- if !<SelfSponsoring<T>>::get(&call.0) {
+ let mode = <Pallet<T>>::sponsoring_mode(call.0);
+ if mode == SponsoringModeT::Disabled {
return None;
}
- if !<Pallet<T>>::allowed(call.0, *who) {
+ if mode == SponsoringModeT::Allowlisted && !<Pallet<T>>::allowed(call.0, *who) {
return None;
}
let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -1,11 +1,14 @@
#![cfg_attr(not(feature = "std"), no_std)]
+use codec::{Decode, Encode, MaxEncodedLen};
pub use pallet::*;
pub use eth::*;
+use scale_info::TypeInfo;
pub mod eth;
#[frame_support::pallet]
pub mod pallet {
+ pub use super::*;
use evm_coder::execution::Result;
use frame_support::pallet_prelude::*;
use sp_core::H160;
@@ -31,10 +34,15 @@
StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;
#[pallet::storage]
+ #[deprecated]
pub(super) type SelfSponsoring<T: Config> =
StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;
#[pallet::storage]
+ pub(super) type SponsoringMode<T: Config> =
+ StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;
+
+ #[pallet::storage]
pub(super) type SponsoringRateLimit<T: Config> = StorageMap<
Hasher = Twox128,
Key = H160,
@@ -68,8 +76,31 @@
>;
impl<T: Config> Pallet<T> {
+ pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {
+ <SponsoringMode<T>>::get(contract)
+ .or_else(|| {
+ <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)
+ })
+ .unwrap_or_default()
+ }
+ pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {
+ if mode == SponsoringModeT::Disabled {
+ <SponsoringMode<T>>::remove(contract);
+ } else {
+ <SponsoringMode<T>>::insert(contract, mode);
+ }
+ <SelfSponsoring<T>>::remove(contract)
+ }
+
pub fn toggle_sponsoring(contract: H160, enabled: bool) {
- <SelfSponsoring<T>>::insert(contract, enabled);
+ Self::set_sponsoring_mode(
+ contract,
+ if enabled {
+ SponsoringModeT::Allowlisted
+ } else {
+ SponsoringModeT::Disabled
+ },
+ )
}
pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {
@@ -94,3 +125,34 @@
}
}
}
+
+#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen)]
+pub enum SponsoringModeT {
+ Disabled,
+ Allowlisted,
+ Generous,
+}
+
+impl SponsoringModeT {
+ fn from_eth(v: u8) -> Option<Self> {
+ Some(match v {
+ 0 => Self::Disabled,
+ 1 => Self::Allowlisted,
+ 2 => Self::Generous,
+ _ => return None,
+ })
+ }
+ fn to_eth(self) -> u8 {
+ match self {
+ SponsoringModeT::Disabled => 0,
+ SponsoringModeT::Allowlisted => 1,
+ SponsoringModeT::Generous => 2,
+ }
+ }
+}
+
+impl Default for SponsoringModeT {
+ fn default() -> Self {
+ Self::Disabled
+ }
+}
pallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/evm-contract-helpers/src/stubs/ContractHelpers.soldiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
+++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
@@ -21,7 +21,7 @@
}
}
-// Selector: 31acb1fe
+// Selector: 7b4866f9
contract ContractHelpers is Dummy, ERC165 {
// Selector: contractOwner(address) 5152b14c
function contractOwner(address contractAddress)
@@ -47,6 +47,8 @@
return false;
}
+ // Deprecated
+ //
// Selector: toggleSponsoring(address,bool) fcac6d86
function toggleSponsoring(address contractAddress, bool enabled) public {
require(false, stub_error);
@@ -55,6 +57,26 @@
dummy = 0;
}
+ // Selector: setSponsoringMode(address,uint8) fde8a560
+ function setSponsoringMode(address contractAddress, uint8 mode) public {
+ require(false, stub_error);
+ contractAddress;
+ mode;
+ dummy = 0;
+ }
+
+ // Selector: sponsoringMode(address) b70c7267
+ function sponsoringMode(address contractAddress)
+ public
+ view
+ returns (uint8)
+ {
+ require(false, stub_error);
+ contractAddress;
+ dummy;
+ return 0;
+ }
+
// Selector: setSponsoringRateLimit(address,uint32) 77b6c908
function setSponsoringRateLimit(address contractAddress, uint32 rateLimit)
public
tests/src/eth/api/ContractHelpers.soldiffbeforeafterboth--- a/tests/src/eth/api/ContractHelpers.sol
+++ b/tests/src/eth/api/ContractHelpers.sol
@@ -12,7 +12,7 @@
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
-// Selector: 31acb1fe
+// Selector: 7b4866f9
interface ContractHelpers is Dummy, ERC165 {
// Selector: contractOwner(address) 5152b14c
function contractOwner(address contractAddress)
@@ -26,9 +26,20 @@
view
returns (bool);
+ // Deprecated
+ //
// Selector: toggleSponsoring(address,bool) fcac6d86
function toggleSponsoring(address contractAddress, bool enabled) external;
+ // Selector: setSponsoringMode(address,uint8) fde8a560
+ function setSponsoringMode(address contractAddress, uint8 mode) external;
+
+ // Selector: sponsoringMode(address) b70c7267
+ function sponsoringMode(address contractAddress)
+ external
+ view
+ returns (uint8);
+
// Selector: setSponsoringRateLimit(address,uint32) 77b6c908
function setSponsoringRateLimit(address contractAddress, uint32 rateLimit)
external;
tests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth--- a/tests/src/eth/contractSponsoring.test.ts
+++ b/tests/src/eth/contractSponsoring.test.ts
@@ -10,7 +10,10 @@
createEthAccountWithBalance,
transferBalanceToEth,
deployFlipper,
- itWeb3} from './util/helpers';
+ itWeb3,
+ SponsoringMode,
+ createEthAccount,
+} from './util/helpers';
describe('Sponsoring EVM contracts', () => {
itWeb3('Sponsoring can be set by the address that has deployed the contract', async ({api, web3}) => {
@@ -18,7 +21,7 @@
const flipper = await deployFlipper(web3, owner);
const helpers = contractHelpers(web3, owner);
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
- await helpers.methods.toggleSponsoring(flipper.options.address, true).send({from: owner});
+ await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
});
@@ -28,11 +31,11 @@
const flipper = await deployFlipper(web3, owner);
const helpers = contractHelpers(web3, owner);
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
- await expect(helpers.methods.toggleSponsoring(notOwner, true).send({from: notOwner})).to.rejected;
+ await expect(helpers.methods.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).send({from: notOwner})).to.rejected;
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
});
- itWeb3('Sponsoring is set, an address that has no UNQ can send a transaction and it works. Sponsor balance should decrease (allowlisted)', async ({api, web3}) => {
+ itWeb3('In generous mode, non-allowlisted user transaction will be sponsored', async ({api, web3}) => {
const alice = privateKey('//Alice');
const owner = await createEthAccountWithBalance(api, web3);
@@ -41,11 +44,39 @@
const flipper = await deployFlipper(web3, owner);
const helpers = contractHelpers(web3, owner);
+
+ expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
+ await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});
+ await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+ expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
+
+ await transferBalanceToEth(api, alice, flipper.options.address);
+
+ const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);
+ expect(originalFlipperBalance).to.be.not.equal('0');
+
+ await flipper.methods.flip().send({from: caller});
+ expect(await flipper.methods.getValue().call()).to.be.true;
+
+ // Balance should be taken from flipper instead of caller
+ const balanceAfter = await web3.eth.getBalance(flipper.options.address);
+ expect(+balanceAfter).to.be.lessThan(+originalFlipperBalance);
+ });
+
+ itWeb3('Sponsoring is set, an address that has no UNQ can send a transaction and it works. Sponsor balance should decrease (allowlisted)', async ({api, web3}) => {
+ const alice = privateKey('//Alice');
+
+ const owner = await createEthAccountWithBalance(api, web3);
+ const caller = createEthAccount(web3);
+
+ const flipper = await deployFlipper(web3, owner);
+
+ const helpers = contractHelpers(web3, owner);
await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
- await helpers.methods.toggleSponsoring(flipper.options.address, true).send({from: owner});
+ await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
@@ -66,14 +97,14 @@
const alice = privateKey('//Alice');
const owner = await createEthAccountWithBalance(api, web3);
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = createEthAccount(web3);
const flipper = await deployFlipper(web3, owner);
const helpers = contractHelpers(web3, owner);
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
- await helpers.methods.toggleSponsoring(flipper.options.address, true).send({from: owner});
+ await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
@@ -104,7 +135,7 @@
await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
- await helpers.methods.toggleSponsoring(flipper.options.address, true).send({from: owner});
+ await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
@@ -133,7 +164,7 @@
await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
- await helpers.methods.toggleSponsoring(flipper.options.address, true).send({from: owner});
+ await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
await helpers.methods.setSponsoringRateLimit(flipper.options.address, 10).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
@@ -157,33 +188,4 @@
const helpers = contractHelpers(web3, owner);
expect(await helpers.methods.getSponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200');
});
-
- itWeb3('If allowlist mode is off and sponsorship is on, sponsorship does not work', async ({api, web3}) => {
- const alice = privateKey('//Alice');
-
- const owner = await createEthAccountWithBalance(api, web3);
- const caller = await createEthAccountWithBalance(api, web3);
-
- const flipper = await deployFlipper(web3, owner);
-
- const helpers = contractHelpers(web3, owner);
-
- expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
- await helpers.methods.toggleSponsoring(flipper.options.address, true).send({from: owner});
- await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
- expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
-
- await transferBalanceToEth(api, alice, flipper.options.address);
-
- const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);
- expect(originalFlipperBalance).to.be.not.equal('0');
-
- await flipper.methods.flip().send({from: caller});
- expect(await flipper.methods.getValue().call()).to.be.true;
-
- // Balance should be taken from flipper instead of caller
- const balanceAfter = await web3.eth.getBalance(flipper.options.address);
- expect(+balanceAfter).to.be.equals(+originalFlipperBalance);
- });
-
});
tests/src/eth/marketplace/marketplace.test.tsdiffbeforeafterboth--- a/tests/src/eth/marketplace/marketplace.test.ts
+++ b/tests/src/eth/marketplace/marketplace.test.ts
@@ -2,7 +2,7 @@
import {getBalanceSingle, transferBalanceExpectSuccess} from '../../substrate/get-balance';
import privateKey from '../../substrate/privateKey';
import {addToAllowListExpectSuccess, confirmSponsorshipExpectSuccess, createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, getTokenOwner, setCollectionLimitsExpectSuccess, setCollectionSponsorExpectSuccess, transferExpectSuccess, transferFromExpectSuccess} from '../../util/helpers';
-import {collectionIdToAddress, contractHelpers, createEthAccountWithBalance, executeEthTxOnSub, GAS_ARGS, itWeb3, subToEth, subToEthLowercase, transferBalanceToEth} from '../util/helpers';
+import {collectionIdToAddress, contractHelpers, createEthAccountWithBalance, executeEthTxOnSub, GAS_ARGS, itWeb3, SponsoringMode, subToEth, subToEthLowercase, transferBalanceToEth} from '../util/helpers';
import {evmToAddress} from '@polkadot/util-crypto';
import nonFungibleAbi from '../nonFungibleAbi.json';
import fungibleAbi from '../fungibleAbi.json';
@@ -20,7 +20,7 @@
});
const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments:[matcherOwner]}).send({from: matcherOwner});
const helpers = contractHelpers(web3, matcherOwner);
- await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});
+ await helpers.methods.setSponsoringMode(matcher.options.address, SponsoringMode.Allowlisted).send({from: matcherOwner});
await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});
await transferBalanceToEth(api, alice, matcher.options.address);
@@ -147,7 +147,7 @@
const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments: [matcherOwner]}).send({from: matcherOwner, gas: 10000000});
await matcher.methods.setEscrow(escrow).send({from: matcherOwner});
const helpers = contractHelpers(web3, matcherOwner);
- await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});
+ await helpers.methods.setSponsoringMode(matcher.options.address, SponsoringMode.Allowlisted).send({from: matcherOwner});
await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});
await transferBalanceToEth(api, alice, matcher.options.address);
tests/src/eth/sponsoring.test.tsdiffbeforeafterboth--- a/tests/src/eth/sponsoring.test.ts
+++ b/tests/src/eth/sponsoring.test.ts
@@ -1,6 +1,6 @@
import {expect} from 'chai';
import privateKey from '../substrate/privateKey';
-import {contractHelpers, createEthAccount, createEthAccountWithBalance, deployCollector, deployFlipper, itWeb3, transferBalanceToEth} from './util/helpers';
+import {contractHelpers, createEthAccount, createEthAccountWithBalance, deployCollector, deployFlipper, itWeb3, SponsoringMode, transferBalanceToEth} from './util/helpers';
describe('EVM sponsoring', () => {
itWeb3('Fee is deducted from contract if sponsoring is enabled', async ({api, web3}) => {
@@ -18,7 +18,7 @@
await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
- await helpers.methods.toggleSponsoring(flipper.options.address, true).send({from: owner});
+ await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
@@ -49,7 +49,7 @@
await helpers.methods.toggleAllowed(collector.options.address, caller, true).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.false;
- await helpers.methods.toggleSponsoring(collector.options.address, true).send({from: owner});
+ await helpers.methods.setSponsoringMode(collector.options.address, SponsoringMode.Allowlisted).send({from: owner});
await helpers.methods.setSponsoringRateLimit(collector.options.address, 0).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.true;
tests/src/eth/util/contractHelpersAbi.jsondiffbeforeafterboth--- a/tests/src/eth/util/contractHelpersAbi.json
+++ b/tests/src/eth/util/contractHelpersAbi.json
@@ -147,6 +147,43 @@
"type": "address"
},
{
+ "internalType": "uint8",
+ "name": "mode",
+ "type": "uint8"
+ }
+ ],
+ "name": "setSponsoringMode",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ }
+ ],
+ "name": "sponsoringMode",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ },
+ {
"internalType": "uint32",
"name": "limit",
"type": "uint32"
@@ -176,4 +213,4 @@
"stateMutability": "view",
"type": "function"
}
-]
\ No newline at end of file
+]
tests/src/eth/util/helpers.tsdiffbeforeafterboth1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56// eslint-disable-next-line @typescript-eslint/triple-slash-reference7/// <reference path="helpers.d.ts" />89import {ApiPromise} from '@polkadot/api';10import {addressToEvm, evmToAddress} from '@polkadot/util-crypto';11import Web3 from 'web3';12import usingApi, {submitTransactionAsync} from '../../substrate/substrate-api';13import {IKeyringPair} from '@polkadot/types/types';14import {expect} from 'chai';15import {getGenericResult, UNIQUE} from '../../util/helpers';16import * as solc from 'solc';17import config from '../../config';18import privateKey from '../../substrate/privateKey';19import contractHelpersAbi from './contractHelpersAbi.json';20import getBalance from '../../substrate/get-balance';2122export const GAS_ARGS = {gas: 2500000};2324let web3Connected = false;25export async function usingWeb3<T>(cb: (web3: Web3) => Promise<T> | T): Promise<T> {26 if (web3Connected) throw new Error('do not nest usingWeb3 calls');27 web3Connected = true;2829 const provider = new Web3.providers.WebsocketProvider(config.substrateUrl);30 const web3 = new Web3(provider);3132 try {33 return await cb(web3);34 } finally {35 // provider.disconnect(3000, 'normal disconnect');36 provider.connection.close();37 web3Connected = false;38 }39}4041export function collectionIdToAddress(address: number): string {42 if (address >= 0xffffffff || address < 0) throw new Error('id overflow');43 const buf = Buffer.from([0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,44 address >> 24,45 (address >> 16) & 0xff,46 (address >> 8) & 0xff,47 address & 0xff,48 ]);49 return Web3.utils.toChecksumAddress('0x' + buf.toString('hex'));50}5152export function createEthAccount(web3: Web3) {53 const account = web3.eth.accounts.create();54 web3.eth.accounts.wallet.add(account.privateKey);55 return account.address;56}5758export async function createEthAccountWithBalance(api: ApiPromise, web3: Web3) {59 const alice = privateKey('//Alice');60 const account = createEthAccount(web3);61 await transferBalanceToEth(api, alice, account);6263 return account;64}6566export async function transferBalanceToEth(api: ApiPromise, source: IKeyringPair, target: string, amount = 1000n * UNIQUE) {67 const tx = api.tx.balances.transfer(evmToAddress(target), amount);68 const events = await submitTransactionAsync(source, tx);69 const result = getGenericResult(events);70 expect(result.success).to.be.true;71}7273export async function itWeb3(name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any, opts: { only?: boolean, skip?: boolean } = {}) {74 let i: any = it;75 if (opts.only) i = i.only;76 else if (opts.skip) i = i.skip;77 i(name, async () => {78 await usingApi(async api => {79 await usingWeb3(async web3 => {80 await cb({api, web3});81 });82 });83 });84}85itWeb3.only = (name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any) => itWeb3(name, cb, {only: true});86itWeb3.skip = (name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any) => itWeb3(name, cb, {skip: true});8788export async function generateSubstrateEthPair(web3: Web3) {89 const account = web3.eth.accounts.create();90 evmToAddress(account.address);91}9293type NormalizedEvent = {94 address: string,95 event: string,96 args: { [key: string]: string }97};9899export function normalizeEvents(events: any): NormalizedEvent[] {100 const output = [];101 for (const key of Object.keys(events)) {102 if (key.match(/^[0-9]+$/)) {103 output.push(events[key]);104 } else if (Array.isArray(events[key])) {105 output.push(...events[key]);106 } else {107 output.push(events[key]);108 }109 }110 output.sort((a, b) => a.logIndex - b.logIndex);111 return output.map(({address, event, returnValues}) => {112 const args: { [key: string]: string } = {};113 for (const key of Object.keys(returnValues)) {114 if (!key.match(/^[0-9]+$/)) {115 args[key] = returnValues[key];116 }117 }118 return {119 address,120 event,121 args,122 };123 });124}125126export async function recordEvents(contract: any, action: () => Promise<void>): Promise<NormalizedEvent[]> {127 const out: any = [];128 contract.events.allEvents((_: any, event: any) => {129 out.push(event);130 });131 await action();132 return normalizeEvents(out);133}134135export function subToEthLowercase(eth: string): string {136 const bytes = addressToEvm(eth);137 return '0x' + Buffer.from(bytes).toString('hex');138}139140export function subToEth(eth: string): string {141 return Web3.utils.toChecksumAddress(subToEthLowercase(eth));142}143144export function compileContract(name: string, src: string) {145 const out = JSON.parse(solc.compile(JSON.stringify({146 language: 'Solidity',147 sources: {148 [`${name}.sol`]: {149 content: `150 // SPDX-License-Identifier: UNLICENSED151 pragma solidity ^0.8.6;152153 ${src}154 `,155 },156 },157 settings: {158 outputSelection: {159 '*': {160 '*': ['*'],161 },162 },163 },164 }))).contracts[`${name}.sol`][name];165166 return {167 abi: out.abi,168 object: '0x' + out.evm.bytecode.object,169 };170}171172export async function deployFlipper(web3: Web3, deployer: string) {173 const compiled = compileContract('Flipper', `174 contract Flipper {175 bool value = false;176 function flip() public {177 value = !value;178 }179 function getValue() public view returns (bool) {180 return value;181 }182 }183 `);184 const flipperContract = new web3.eth.Contract(compiled.abi, undefined, {185 data: compiled.object,186 from: deployer,187 ...GAS_ARGS,188 });189 const flipper = await flipperContract.deploy({data: compiled.object}).send({from: deployer});190191 return flipper;192}193194export async function deployCollector(web3: Web3, deployer: string) {195 const compiled = compileContract('Collector', `196 contract Collector {197 uint256 collected;198 fallback() external payable {199 giveMoney();200 }201 function giveMoney() public payable {202 collected += msg.value;203 }204 function getCollected() public view returns (uint256) {205 return collected;206 }207 function getUnaccounted() public view returns (uint256) {208 return address(this).balance - collected;209 }210211 function withdraw(address payable target) public {212 target.transfer(collected);213 collected = 0;214 }215 }216 `);217 const collectorContract = new web3.eth.Contract(compiled.abi, undefined, {218 data: compiled.object,219 from: deployer,220 ...GAS_ARGS,221 });222 const collector = await collectorContract.deploy({data: compiled.object}).send({from: deployer});223224 return collector;225}226227export function contractHelpers(web3: Web3, caller: string) {228 return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});229}230231/**232 * Execute ethereum method call using substrate account233 * @param to target contract234 * @param mkTx - closure, receiving `contract.methods`, and returning method call,235 * to be used as following (assuming `to` = erc20 contract):236 * `m => m.transfer(to, amount)`237 *238 * # Example239 * ```ts240 * executeEthTxOnSub(api, alice, erc20Contract, m => m.transfer(target, amount));241 * ```242 */243export async function executeEthTxOnSub(web3: Web3, api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {244 const tx = api.tx.evm.call(245 subToEth(from.address),246 to.options.address,247 mkTx(to.methods).encodeABI(),248 value,249 GAS_ARGS.gas,250 await web3.eth.getGasPrice(),251 null,252 null,253 [],254 );255 const events = await submitTransactionAsync(from, tx);256 expect(events.some(({event: {section, method}}) => section == 'evm' && method == 'Executed')).to.be.true;257}258259export async function ethBalanceViaSub(api: ApiPromise, address: string): Promise<bigint> {260 return (await getBalance(api, [evmToAddress(address)]))[0];261}262263/**264 * Measure how much gas given closure consumes265 *266 * @param user which user balance will be checked267 */268export async function recordEthFee(api: ApiPromise, user: string, call: () => Promise<any>): Promise<bigint> {269 const before = await ethBalanceViaSub(api, user);270271 await call();272273 const after = await ethBalanceViaSub(api, user);274275 // Can't use .to.be.less, because chai doesn't supports bigint276 expect(after < before).to.be.true;277278 return before - after;279}1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56// eslint-disable-next-line @typescript-eslint/triple-slash-reference7/// <reference path="helpers.d.ts" />89import {ApiPromise} from '@polkadot/api';10import {addressToEvm, evmToAddress} from '@polkadot/util-crypto';11import Web3 from 'web3';12import usingApi, {submitTransactionAsync} from '../../substrate/substrate-api';13import {IKeyringPair} from '@polkadot/types/types';14import {expect} from 'chai';15import {getGenericResult, UNIQUE} from '../../util/helpers';16import * as solc from 'solc';17import config from '../../config';18import privateKey from '../../substrate/privateKey';19import contractHelpersAbi from './contractHelpersAbi.json';20import getBalance from '../../substrate/get-balance';2122export const GAS_ARGS = {gas: 2500000};2324export enum SponsoringMode {25 Disabled = 0,26 Allowlisted = 1,27 Generous = 2,28}2930let web3Connected = false;31export async function usingWeb3<T>(cb: (web3: Web3) => Promise<T> | T): Promise<T> {32 if (web3Connected) throw new Error('do not nest usingWeb3 calls');33 web3Connected = true;3435 const provider = new Web3.providers.WebsocketProvider(config.substrateUrl);36 const web3 = new Web3(provider);3738 try {39 return await cb(web3);40 } finally {41 // provider.disconnect(3000, 'normal disconnect');42 provider.connection.close();43 web3Connected = false;44 }45}4647export function collectionIdToAddress(address: number): string {48 if (address >= 0xffffffff || address < 0) throw new Error('id overflow');49 const buf = Buffer.from([0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,50 address >> 24,51 (address >> 16) & 0xff,52 (address >> 8) & 0xff,53 address & 0xff,54 ]);55 return Web3.utils.toChecksumAddress('0x' + buf.toString('hex'));56}5758export function createEthAccount(web3: Web3) {59 const account = web3.eth.accounts.create();60 web3.eth.accounts.wallet.add(account.privateKey);61 return account.address;62}6364export async function createEthAccountWithBalance(api: ApiPromise, web3: Web3) {65 const alice = privateKey('//Alice');66 const account = createEthAccount(web3);67 await transferBalanceToEth(api, alice, account);6869 return account;70}7172export async function transferBalanceToEth(api: ApiPromise, source: IKeyringPair, target: string, amount = 1000n * UNIQUE) {73 const tx = api.tx.balances.transfer(evmToAddress(target), amount);74 const events = await submitTransactionAsync(source, tx);75 const result = getGenericResult(events);76 expect(result.success).to.be.true;77}7879export async function itWeb3(name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any, opts: { only?: boolean, skip?: boolean } = {}) {80 let i: any = it;81 if (opts.only) i = i.only;82 else if (opts.skip) i = i.skip;83 i(name, async () => {84 await usingApi(async api => {85 await usingWeb3(async web3 => {86 await cb({api, web3});87 });88 });89 });90}91itWeb3.only = (name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any) => itWeb3(name, cb, {only: true});92itWeb3.skip = (name: string, cb: (apis: { web3: Web3, api: ApiPromise }) => any) => itWeb3(name, cb, {skip: true});9394export async function generateSubstrateEthPair(web3: Web3) {95 const account = web3.eth.accounts.create();96 evmToAddress(account.address);97}9899type NormalizedEvent = {100 address: string,101 event: string,102 args: { [key: string]: string }103};104105export function normalizeEvents(events: any): NormalizedEvent[] {106 const output = [];107 for (const key of Object.keys(events)) {108 if (key.match(/^[0-9]+$/)) {109 output.push(events[key]);110 } else if (Array.isArray(events[key])) {111 output.push(...events[key]);112 } else {113 output.push(events[key]);114 }115 }116 output.sort((a, b) => a.logIndex - b.logIndex);117 return output.map(({address, event, returnValues}) => {118 const args: { [key: string]: string } = {};119 for (const key of Object.keys(returnValues)) {120 if (!key.match(/^[0-9]+$/)) {121 args[key] = returnValues[key];122 }123 }124 return {125 address,126 event,127 args,128 };129 });130}131132export async function recordEvents(contract: any, action: () => Promise<void>): Promise<NormalizedEvent[]> {133 const out: any = [];134 contract.events.allEvents((_: any, event: any) => {135 out.push(event);136 });137 await action();138 return normalizeEvents(out);139}140141export function subToEthLowercase(eth: string): string {142 const bytes = addressToEvm(eth);143 return '0x' + Buffer.from(bytes).toString('hex');144}145146export function subToEth(eth: string): string {147 return Web3.utils.toChecksumAddress(subToEthLowercase(eth));148}149150export function compileContract(name: string, src: string) {151 const out = JSON.parse(solc.compile(JSON.stringify({152 language: 'Solidity',153 sources: {154 [`${name}.sol`]: {155 content: `156 // SPDX-License-Identifier: UNLICENSED157 pragma solidity ^0.8.6;158159 ${src}160 `,161 },162 },163 settings: {164 outputSelection: {165 '*': {166 '*': ['*'],167 },168 },169 },170 }))).contracts[`${name}.sol`][name];171172 return {173 abi: out.abi,174 object: '0x' + out.evm.bytecode.object,175 };176}177178export async function deployFlipper(web3: Web3, deployer: string) {179 const compiled = compileContract('Flipper', `180 contract Flipper {181 bool value = false;182 function flip() public {183 value = !value;184 }185 function getValue() public view returns (bool) {186 return value;187 }188 }189 `);190 const flipperContract = new web3.eth.Contract(compiled.abi, undefined, {191 data: compiled.object,192 from: deployer,193 ...GAS_ARGS,194 });195 const flipper = await flipperContract.deploy({data: compiled.object}).send({from: deployer});196197 return flipper;198}199200export async function deployCollector(web3: Web3, deployer: string) {201 const compiled = compileContract('Collector', `202 contract Collector {203 uint256 collected;204 fallback() external payable {205 giveMoney();206 }207 function giveMoney() public payable {208 collected += msg.value;209 }210 function getCollected() public view returns (uint256) {211 return collected;212 }213 function getUnaccounted() public view returns (uint256) {214 return address(this).balance - collected;215 }216217 function withdraw(address payable target) public {218 target.transfer(collected);219 collected = 0;220 }221 }222 `);223 const collectorContract = new web3.eth.Contract(compiled.abi, undefined, {224 data: compiled.object,225 from: deployer,226 ...GAS_ARGS,227 });228 const collector = await collectorContract.deploy({data: compiled.object}).send({from: deployer});229230 return collector;231}232233export function contractHelpers(web3: Web3, caller: string) {234 return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});235}236237/**238 * Execute ethereum method call using substrate account239 * @param to target contract240 * @param mkTx - closure, receiving `contract.methods`, and returning method call,241 * to be used as following (assuming `to` = erc20 contract):242 * `m => m.transfer(to, amount)`243 *244 * # Example245 * ```ts246 * executeEthTxOnSub(api, alice, erc20Contract, m => m.transfer(target, amount));247 * ```248 */249export async function executeEthTxOnSub(web3: Web3, api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {250 const tx = api.tx.evm.call(251 subToEth(from.address),252 to.options.address,253 mkTx(to.methods).encodeABI(),254 value,255 GAS_ARGS.gas,256 await web3.eth.getGasPrice(),257 null,258 null,259 [],260 );261 const events = await submitTransactionAsync(from, tx);262 expect(events.some(({event: {section, method}}) => section == 'evm' && method == 'Executed')).to.be.true;263}264265export async function ethBalanceViaSub(api: ApiPromise, address: string): Promise<bigint> {266 return (await getBalance(api, [evmToAddress(address)]))[0];267}268269/**270 * Measure how much gas given closure consumes271 *272 * @param user which user balance will be checked273 */274export async function recordEthFee(api: ApiPromise, user: string, call: () => Promise<any>): Promise<bigint> {275 const before = await ethBalanceViaSub(api, user);276277 await call();278279 const after = await ethBalanceViaSub(api, user);280281 // Can't use .to.be.less, because chai doesn't supports bigint282 expect(after < before).to.be.true;283284 return before - after;285}