git.delta.rocks / unique-network / refs/commits / 999c24f384bf

difftreelog

Merge pull request #469 from UniqueNetwork/feature/refactor_contract_sponsoring

Yaroslav Bolyukin2022-08-25parents: #cdbbf09 #e80fd26.patch.diff
in: master
Feature/refactor contract sponsoring

19 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2345,7 +2345,7 @@
 [[package]]
 name = "fc-consensus"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "async-trait",
  "fc-db",
@@ -2364,7 +2364,7 @@
 [[package]]
 name = "fc-db"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "fp-storage",
  "kvdb-rocksdb",
@@ -2380,7 +2380,7 @@
 [[package]]
 name = "fc-mapping-sync"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "fc-db",
  "fp-consensus",
@@ -2397,7 +2397,7 @@
 [[package]]
 name = "fc-rpc"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "ethereum",
  "ethereum-types",
@@ -2437,7 +2437,7 @@
 [[package]]
 name = "fc-rpc-core"
 version = "1.1.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "ethereum",
  "ethereum-types",
@@ -2578,7 +2578,7 @@
 [[package]]
 name = "fp-consensus"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "ethereum",
  "parity-scale-codec 3.1.5",
@@ -2590,7 +2590,7 @@
 [[package]]
 name = "fp-evm"
 version = "3.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "evm",
  "frame-support",
@@ -2604,7 +2604,7 @@
 [[package]]
 name = "fp-evm-mapping"
 version = "0.1.0"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "frame-support",
  "sp-core",
@@ -2613,7 +2613,7 @@
 [[package]]
 name = "fp-rpc"
 version = "3.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "ethereum",
  "ethereum-types",
@@ -2630,7 +2630,7 @@
 [[package]]
 name = "fp-self-contained"
 version = "1.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "ethereum",
  "frame-support",
@@ -2646,7 +2646,7 @@
 [[package]]
 name = "fp-storage"
 version = "2.0.0"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 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#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "fp-evm",
  "frame-support",
@@ -5552,7 +5552,7 @@
 
 [[package]]
 name = "pallet-common"
-version = "0.1.6"
+version = "0.1.7"
 dependencies = [
  "ethereum",
  "evm-coder",
@@ -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#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 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#64915c0986fcf2bbe942794b838c0cf359c93c21"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c"
 dependencies = [
  "evm",
  "fp-evm",
@@ -5733,7 +5733,7 @@
 
 [[package]]
 name = "pallet-evm-contract-helpers"
-version = "0.1.2"
+version = "0.2.0"
 dependencies = [
  "evm-coder",
  "fp-evm-mapping",
@@ -12130,7 +12130,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if 0.1.10",
  "digest 0.10.3",
  "rand 0.8.5",
  "static_assertions",
modifiedcrates/evm-coder/CHANGELOG.mddiffbeforeafterboth
--- a/crates/evm-coder/CHANGELOG.md
+++ b/crates/evm-coder/CHANGELOG.md
@@ -1,4 +1,16 @@
-<!-- bureaucrate goes here -->
+# Change Log
+
+All notable changes to this project will be documented in this file.
+## [v0.1.2] 2022-08-19
+
+### Added
+
+ - Implementation `AbiWrite` for tuples.
+
+ ### Fixes
+
+ - Tuple generation for solidity.
+
 ## [v0.1.1] 2022-08-16
 
 ### Other changes
modifiedcrates/evm-coder/src/abi.rsdiffbeforeafterboth
before · crates/evm-coder/src/abi.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! TODO: I misunterstood therminology, abi IS rlp encoded, so18//! this module should be replaced with rlp crate1920#![allow(dead_code)]2122#[cfg(not(feature = "std"))]23use alloc::vec::Vec;24use evm_core::ExitError;25use primitive_types::{H160, U256};2627use crate::{28	execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},29	types::{string, self},30};31use crate::execution::Result;3233const ABI_ALIGNMENT: usize = 32;3435#[derive(Clone)]36pub struct AbiReader<'i> {37	buf: &'i [u8],38	subresult_offset: usize,39	offset: usize,40}41impl<'i> AbiReader<'i> {42	pub fn new(buf: &'i [u8]) -> Self {43		Self {44			buf,45			subresult_offset: 0,46			offset: 0,47		}48	}49	pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> {50		if buf.len() < 4 {51			return Err(Error::Error(ExitError::OutOfOffset));52		}53		let mut method_id = [0; 4];54		method_id.copy_from_slice(&buf[0..4]);5556		Ok((57			method_id,58			Self {59				buf,60				subresult_offset: 4,61				offset: 4,62			},63		))64	}6566	fn read_pad<const S: usize>(67		buf: &[u8],68		offset: usize,69		pad_start: usize,70		pad_size: usize,71		block_start: usize,72		block_size: usize,73	) -> Result<[u8; S]> {74		if buf.len() - offset < ABI_ALIGNMENT {75			return Err(Error::Error(ExitError::OutOfOffset));76		}77		let mut block = [0; S];78		// Verify padding is empty79		if !buf[pad_start..pad_size].iter().all(|&v| v == 0) {80			return Err(Error::Error(ExitError::InvalidRange));81		}82		block.copy_from_slice(&buf[block_start..block_size]);83		Ok(block)84	}8586	fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {87		let offset = self.offset;88		self.offset += ABI_ALIGNMENT;89		Self::read_pad(90			self.buf,91			offset,92			offset,93			offset + ABI_ALIGNMENT - S,94			offset + ABI_ALIGNMENT - S,95			offset + ABI_ALIGNMENT,96		)97	}9899	fn read_padright<const S: usize>(&mut self) -> Result<[u8; S]> {100		let offset = self.offset;101		self.offset += ABI_ALIGNMENT;102		Self::read_pad(103			self.buf,104			offset,105			offset + S,106			offset + ABI_ALIGNMENT,107			offset,108			offset + S,109		)110	}111112	pub fn address(&mut self) -> Result<H160> {113		Ok(H160(self.read_padleft()?))114	}115116	pub fn bool(&mut self) -> Result<bool> {117		let data: [u8; 1] = self.read_padleft()?;118		match data[0] {119			0 => Ok(false),120			1 => Ok(true),121			_ => Err(Error::Error(ExitError::InvalidRange)),122		}123	}124125	pub fn bytes4(&mut self) -> Result<[u8; 4]> {126		self.read_padright()127	}128129	pub fn bytes(&mut self) -> Result<Vec<u8>> {130		let mut subresult = self.subresult()?;131		let length = subresult.read_usize()?;132		if subresult.buf.len() < subresult.offset + length {133			return Err(Error::Error(ExitError::OutOfOffset));134		}135		Ok(subresult.buf[subresult.offset..subresult.offset + length].into())136	}137	pub fn string(&mut self) -> Result<string> {138		string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))139	}140141	pub fn uint8(&mut self) -> Result<u8> {142		Ok(self.read_padleft::<1>()?[0])143	}144145	pub fn uint32(&mut self) -> Result<u32> {146		Ok(u32::from_be_bytes(self.read_padleft()?))147	}148149	pub fn uint128(&mut self) -> Result<u128> {150		Ok(u128::from_be_bytes(self.read_padleft()?))151	}152153	pub fn uint256(&mut self) -> Result<U256> {154		let buf: [u8; 32] = self.read_padleft()?;155		Ok(U256::from_big_endian(&buf))156	}157158	pub fn uint64(&mut self) -> Result<u64> {159		Ok(u64::from_be_bytes(self.read_padleft()?))160	}161162	pub fn read_usize(&mut self) -> Result<usize> {163		Ok(usize::from_be_bytes(self.read_padleft()?))164	}165166	fn subresult(&mut self) -> Result<AbiReader<'i>> {167		let offset = self.read_usize()?;168		if offset + self.subresult_offset > self.buf.len() {169			return Err(Error::Error(ExitError::InvalidRange));170		}171		Ok(AbiReader {172			buf: self.buf,173			subresult_offset: offset + self.subresult_offset,174			offset: offset + self.subresult_offset,175		})176	}177178	pub fn is_finished(&self) -> bool {179		self.buf.len() == self.offset180	}181}182183#[derive(Default)]184pub struct AbiWriter {185	static_part: Vec<u8>,186	dynamic_part: Vec<(usize, AbiWriter)>,187	had_call: bool,188}189impl AbiWriter {190	pub fn new() -> Self {191		Self::default()192	}193	pub fn new_call(method_id: u32) -> Self {194		let mut val = Self::new();195		val.static_part.extend(&method_id.to_be_bytes());196		val.had_call = true;197		val198	}199200	fn write_padleft(&mut self, block: &[u8]) {201		assert!(block.len() <= ABI_ALIGNMENT);202		self.static_part203			.extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]);204		self.static_part.extend(block);205	}206207	fn write_padright(&mut self, bytes: &[u8]) {208		assert!(bytes.len() <= ABI_ALIGNMENT);209		self.static_part.extend(bytes);210		self.static_part211			.extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]);212	}213214	pub fn address(&mut self, address: &H160) {215		self.write_padleft(&address.0)216	}217218	pub fn bool(&mut self, value: &bool) {219		self.write_padleft(&[if *value { 1 } else { 0 }])220	}221222	pub fn uint8(&mut self, value: &u8) {223		self.write_padleft(&[*value])224	}225226	pub fn uint32(&mut self, value: &u32) {227		self.write_padleft(&u32::to_be_bytes(*value))228	}229230	pub fn uint128(&mut self, value: &u128) {231		self.write_padleft(&u128::to_be_bytes(*value))232	}233234	pub fn uint256(&mut self, value: &U256) {235		let mut out = [0; 32];236		value.to_big_endian(&mut out);237		self.write_padleft(&out)238	}239240	pub fn write_usize(&mut self, value: &usize) {241		self.write_padleft(&usize::to_be_bytes(*value))242	}243244	pub fn write_subresult(&mut self, result: Self) {245		self.dynamic_part.push((self.static_part.len(), result));246		// Empty block, to be filled later247		self.write_padleft(&[]);248	}249250	pub fn memory(&mut self, value: &[u8]) {251		let mut sub = Self::new();252		sub.write_usize(&value.len());253		for chunk in value.chunks(ABI_ALIGNMENT) {254			sub.write_padright(chunk);255		}256		self.write_subresult(sub);257	}258259	pub fn string(&mut self, value: &str) {260		self.memory(value.as_bytes())261	}262263	pub fn bytes(&mut self, value: &[u8]) {264		self.memory(value)265	}266267	pub fn finish(mut self) -> Vec<u8> {268		for (static_offset, part) in self.dynamic_part {269			let part_offset = self.static_part.len() - self.had_call.then(|| 4).unwrap_or(0);270271			let encoded_dynamic_offset = usize::to_be_bytes(part_offset);272			self.static_part[static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len()273				..static_offset + ABI_ALIGNMENT]274				.copy_from_slice(&encoded_dynamic_offset);275			self.static_part.extend(part.finish())276		}277		self.static_part278	}279}280281pub trait AbiRead<T> {282	fn abi_read(&mut self) -> Result<T>;283}284285macro_rules! impl_abi_readable {286	($ty:ty, $method:ident) => {287		impl AbiRead<$ty> for AbiReader<'_> {288			fn abi_read(&mut self) -> Result<$ty> {289				self.$method()290			}291		}292	};293}294295impl_abi_readable!(u8, uint8);296impl_abi_readable!(u32, uint32);297impl_abi_readable!(u64, uint64);298impl_abi_readable!(u128, uint128);299impl_abi_readable!(U256, uint256);300impl_abi_readable!([u8; 4], bytes4);301impl_abi_readable!(H160, address);302impl_abi_readable!(Vec<u8>, bytes);303impl_abi_readable!(bool, bool);304impl_abi_readable!(string, string);305306mod sealed {307	/// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead308	pub trait CanBePlacedInVec {}309}310311impl sealed::CanBePlacedInVec for U256 {}312impl sealed::CanBePlacedInVec for string {}313impl sealed::CanBePlacedInVec for H160 {}314315impl<R: sealed::CanBePlacedInVec> AbiRead<Vec<R>> for AbiReader<'_>316where317	Self: AbiRead<R>,318{319	fn abi_read(&mut self) -> Result<Vec<R>> {320		let mut sub = self.subresult()?;321		let size = sub.read_usize()?;322		sub.subresult_offset = sub.offset;323		let mut out = Vec::with_capacity(size);324		for _ in 0..size {325			out.push(<Self as AbiRead<R>>::abi_read(&mut sub)?);326		}327		Ok(out)328	}329}330331macro_rules! impl_tuples {332	($($ident:ident)+) => {333		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}334		impl<$($ident),+> AbiRead<($($ident,)+)> for AbiReader<'_>335		where336			$(Self: AbiRead<$ident>),+337		{338			fn abi_read(&mut self) -> Result<($($ident,)+)> {339				let mut subresult = self.subresult()?;340				Ok((341					$(<Self as AbiRead<$ident>>::abi_read(&mut subresult)?,)+342				))343			}344		}345	};346}347348impl_tuples! {A}349impl_tuples! {A B}350impl_tuples! {A B C}351impl_tuples! {A B C D}352impl_tuples! {A B C D E}353impl_tuples! {A B C D E F}354impl_tuples! {A B C D E F G}355impl_tuples! {A B C D E F G H}356impl_tuples! {A B C D E F G H I}357impl_tuples! {A B C D E F G H I J}358359pub trait AbiWrite {360	fn abi_write(&self, writer: &mut AbiWriter);361	fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {362		let mut writer = AbiWriter::new();363		self.abi_write(&mut writer);364		Ok(writer.into())365	}366}367368impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {369	// this particular AbiWrite implementation should be split to another trait,370	// which only implements [`to_result`]371	//372	// But due to lack of specialization feature in stable Rust, we can't have373	// blanket impl of this trait `for T where T: AbiWrite`, so here we abusing374	// default trait methods for it375	fn abi_write(&self, _writer: &mut AbiWriter) {376		debug_assert!(false, "shouldn't be called, see comment")377	}378	fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {379		match self {380			Ok(v) => Ok(WithPostDispatchInfo {381				post_info: v.post_info.clone(),382				data: {383					let mut out = AbiWriter::new();384					v.data.abi_write(&mut out);385					out386				},387			}),388			Err(e) => Err(e.clone()),389		}390	}391}392393macro_rules! impl_abi_writeable {394	($ty:ty, $method:ident) => {395		impl AbiWrite for $ty {396			fn abi_write(&self, writer: &mut AbiWriter) {397				writer.$method(&self)398			}399		}400	};401}402403impl_abi_writeable!(u8, uint8);404impl_abi_writeable!(u32, uint32);405impl_abi_writeable!(u128, uint128);406impl_abi_writeable!(U256, uint256);407impl_abi_writeable!(H160, address);408impl_abi_writeable!(bool, bool);409impl_abi_writeable!(&str, string);410impl AbiWrite for &string {411	fn abi_write(&self, writer: &mut AbiWriter) {412		writer.string(self)413	}414}415impl AbiWrite for &Vec<u8> {416	fn abi_write(&self, writer: &mut AbiWriter) {417		writer.bytes(self)418	}419}420421impl AbiWrite for () {422	fn abi_write(&self, _writer: &mut AbiWriter) {}423}424425#[macro_export]426macro_rules! abi_decode {427	($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => {428		$(429			let $name = $reader.$typ()?;430		)+431	}432}433#[macro_export]434macro_rules! abi_encode {435	($($typ:ident($value:expr)),* $(,)?) => {{436		#[allow(unused_mut)]437		let mut writer = ::evm_coder::abi::AbiWriter::new();438		$(439			writer.$typ($value);440		)*441		writer442	}};443	(call $val:expr; $($typ:ident($value:expr)),* $(,)?) => {{444		#[allow(unused_mut)]445		let mut writer = ::evm_coder::abi::AbiWriter::new_call($val);446		$(447			writer.$typ($value);448		)*449		writer450	}}451}452453#[cfg(test)]454pub mod test {455	use crate::{456		abi::AbiRead,457		types::{string, uint256},458	};459460	use super::{AbiReader, AbiWriter};461	use hex_literal::hex;462463	#[test]464	fn dynamic_after_static() {465		let mut encoder = AbiWriter::new();466		encoder.bool(&true);467		encoder.string("test");468		let encoded = encoder.finish();469470		let mut encoder = AbiWriter::new();471		encoder.bool(&true);472		// Offset to subresult473		encoder.uint32(&(32 * 2));474		// Len of "test"475		encoder.uint32(&4);476		encoder.write_padright(&[b't', b'e', b's', b't']);477		let alternative_encoded = encoder.finish();478479		assert_eq!(encoded, alternative_encoded);480481		let mut decoder = AbiReader::new(&encoded);482		assert_eq!(decoder.bool().unwrap(), true);483		assert_eq!(decoder.string().unwrap(), "test");484	}485486	#[test]487	fn mint_sample() {488		let (call, mut decoder) = AbiReader::new_call(&hex!(489			"490				50bb4e7f491				000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374492				0000000000000000000000000000000000000000000000000000000000000001493				0000000000000000000000000000000000000000000000000000000000000060494				0000000000000000000000000000000000000000000000000000000000000008495				5465737420555249000000000000000000000000000000000000000000000000496			"497		))498		.unwrap();499		assert_eq!(call, u32::to_be_bytes(0x50bb4e7f));500		assert_eq!(501			format!("{:?}", decoder.address().unwrap()),502			"0xad2c0954693c2b5404b7e50967d3481bea432374"503		);504		assert_eq!(decoder.uint32().unwrap(), 1);505		assert_eq!(decoder.string().unwrap(), "Test URI");506	}507508	#[test]509	fn mint_bulk() {510		let (call, mut decoder) = AbiReader::new_call(&hex!(511			"512				36543006513				00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address514				0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[]515				0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]516517				0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem518				00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem519				0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem520521				0000000000000000000000000000000000000000000000000000000000000001 // first token id?   					#60522				0000000000000000000000000000000000000000000000000000000000000040 // offset of string523				000000000000000000000000000000000000000000000000000000000000000a // size of string524				5465737420555249203000000000000000000000000000000000000000000000 // string525526				000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11?			#e0527				0000000000000000000000000000000000000000000000000000000000000040 // offset of string528				000000000000000000000000000000000000000000000000000000000000000a // size of string529				5465737420555249203100000000000000000000000000000000000000000000 // string530531				000000000000000000000000000000000000000000000000000000000000000c // third token id?  Why ==12?			#160532				0000000000000000000000000000000000000000000000000000000000000040 // offset of string533				000000000000000000000000000000000000000000000000000000000000000a // size of string534				5465737420555249203200000000000000000000000000000000000000000000 // string535			"536		))537		.unwrap();538		assert_eq!(call, u32::to_be_bytes(0x36543006));539		let _ = decoder.address().unwrap();540		let data =541			<AbiReader<'_> as AbiRead<Vec<(uint256, string)>>>::abi_read(&mut decoder).unwrap();542		assert_eq!(543			data,544			vec![545				(1.into(), "Test URI 0".to_string()),546				(11.into(), "Test URI 1".to_string()),547				(12.into(), "Test URI 2".to_string())548			]549		);550	}551}
after · crates/evm-coder/src/abi.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! TODO: I misunterstood therminology, abi IS rlp encoded, so18//! this module should be replaced with rlp crate1920#![allow(dead_code)]2122#[cfg(not(feature = "std"))]23use alloc::vec::Vec;24use evm_core::ExitError;25use primitive_types::{H160, U256};2627use crate::{28	execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},29	types::{string, self},30};31use crate::execution::Result;3233const ABI_ALIGNMENT: usize = 32;3435#[derive(Clone)]36pub struct AbiReader<'i> {37	buf: &'i [u8],38	subresult_offset: usize,39	offset: usize,40}41impl<'i> AbiReader<'i> {42	pub fn new(buf: &'i [u8]) -> Self {43		Self {44			buf,45			subresult_offset: 0,46			offset: 0,47		}48	}49	pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> {50		if buf.len() < 4 {51			return Err(Error::Error(ExitError::OutOfOffset));52		}53		let mut method_id = [0; 4];54		method_id.copy_from_slice(&buf[0..4]);5556		Ok((57			method_id,58			Self {59				buf,60				subresult_offset: 4,61				offset: 4,62			},63		))64	}6566	fn read_pad<const S: usize>(67		buf: &[u8],68		offset: usize,69		pad_start: usize,70		pad_size: usize,71		block_start: usize,72		block_size: usize,73	) -> Result<[u8; S]> {74		if buf.len() - offset < ABI_ALIGNMENT {75			return Err(Error::Error(ExitError::OutOfOffset));76		}77		let mut block = [0; S];78		// Verify padding is empty79		if !buf[pad_start..pad_size].iter().all(|&v| v == 0) {80			return Err(Error::Error(ExitError::InvalidRange));81		}82		block.copy_from_slice(&buf[block_start..block_size]);83		Ok(block)84	}8586	fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {87		let offset = self.offset;88		self.offset += ABI_ALIGNMENT;89		Self::read_pad(90			self.buf,91			offset,92			offset,93			offset + ABI_ALIGNMENT - S,94			offset + ABI_ALIGNMENT - S,95			offset + ABI_ALIGNMENT,96		)97	}9899	fn read_padright<const S: usize>(&mut self) -> Result<[u8; S]> {100		let offset = self.offset;101		self.offset += ABI_ALIGNMENT;102		Self::read_pad(103			self.buf,104			offset,105			offset + S,106			offset + ABI_ALIGNMENT,107			offset,108			offset + S,109		)110	}111112	pub fn address(&mut self) -> Result<H160> {113		Ok(H160(self.read_padleft()?))114	}115116	pub fn bool(&mut self) -> Result<bool> {117		let data: [u8; 1] = self.read_padleft()?;118		match data[0] {119			0 => Ok(false),120			1 => Ok(true),121			_ => Err(Error::Error(ExitError::InvalidRange)),122		}123	}124125	pub fn bytes4(&mut self) -> Result<[u8; 4]> {126		self.read_padright()127	}128129	pub fn bytes(&mut self) -> Result<Vec<u8>> {130		let mut subresult = self.subresult()?;131		let length = subresult.read_usize()?;132		if subresult.buf.len() < subresult.offset + length {133			return Err(Error::Error(ExitError::OutOfOffset));134		}135		Ok(subresult.buf[subresult.offset..subresult.offset + length].into())136	}137	pub fn string(&mut self) -> Result<string> {138		string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))139	}140141	pub fn uint8(&mut self) -> Result<u8> {142		Ok(self.read_padleft::<1>()?[0])143	}144145	pub fn uint32(&mut self) -> Result<u32> {146		Ok(u32::from_be_bytes(self.read_padleft()?))147	}148149	pub fn uint128(&mut self) -> Result<u128> {150		Ok(u128::from_be_bytes(self.read_padleft()?))151	}152153	pub fn uint256(&mut self) -> Result<U256> {154		let buf: [u8; 32] = self.read_padleft()?;155		Ok(U256::from_big_endian(&buf))156	}157158	pub fn uint64(&mut self) -> Result<u64> {159		Ok(u64::from_be_bytes(self.read_padleft()?))160	}161162	pub fn read_usize(&mut self) -> Result<usize> {163		Ok(usize::from_be_bytes(self.read_padleft()?))164	}165166	fn subresult(&mut self) -> Result<AbiReader<'i>> {167		let offset = self.read_usize()?;168		if offset + self.subresult_offset > self.buf.len() {169			return Err(Error::Error(ExitError::InvalidRange));170		}171		Ok(AbiReader {172			buf: self.buf,173			subresult_offset: offset + self.subresult_offset,174			offset: offset + self.subresult_offset,175		})176	}177178	pub fn is_finished(&self) -> bool {179		self.buf.len() == self.offset180	}181}182183#[derive(Default)]184pub struct AbiWriter {185	static_part: Vec<u8>,186	dynamic_part: Vec<(usize, AbiWriter)>,187	had_call: bool,188}189impl AbiWriter {190	pub fn new() -> Self {191		Self::default()192	}193	pub fn new_call(method_id: u32) -> Self {194		let mut val = Self::new();195		val.static_part.extend(&method_id.to_be_bytes());196		val.had_call = true;197		val198	}199200	fn write_padleft(&mut self, block: &[u8]) {201		assert!(block.len() <= ABI_ALIGNMENT);202		self.static_part203			.extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]);204		self.static_part.extend(block);205	}206207	fn write_padright(&mut self, bytes: &[u8]) {208		assert!(bytes.len() <= ABI_ALIGNMENT);209		self.static_part.extend(bytes);210		self.static_part211			.extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]);212	}213214	pub fn address(&mut self, address: &H160) {215		self.write_padleft(&address.0)216	}217218	pub fn bool(&mut self, value: &bool) {219		self.write_padleft(&[if *value { 1 } else { 0 }])220	}221222	pub fn uint8(&mut self, value: &u8) {223		self.write_padleft(&[*value])224	}225226	pub fn uint32(&mut self, value: &u32) {227		self.write_padleft(&u32::to_be_bytes(*value))228	}229230	pub fn uint128(&mut self, value: &u128) {231		self.write_padleft(&u128::to_be_bytes(*value))232	}233234	pub fn uint256(&mut self, value: &U256) {235		let mut out = [0; 32];236		value.to_big_endian(&mut out);237		self.write_padleft(&out)238	}239240	pub fn write_usize(&mut self, value: &usize) {241		self.write_padleft(&usize::to_be_bytes(*value))242	}243244	pub fn write_subresult(&mut self, result: Self) {245		self.dynamic_part.push((self.static_part.len(), result));246		// Empty block, to be filled later247		self.write_padleft(&[]);248	}249250	pub fn memory(&mut self, value: &[u8]) {251		let mut sub = Self::new();252		sub.write_usize(&value.len());253		for chunk in value.chunks(ABI_ALIGNMENT) {254			sub.write_padright(chunk);255		}256		self.write_subresult(sub);257	}258259	pub fn string(&mut self, value: &str) {260		self.memory(value.as_bytes())261	}262263	pub fn bytes(&mut self, value: &[u8]) {264		self.memory(value)265	}266267	pub fn finish(mut self) -> Vec<u8> {268		for (static_offset, part) in self.dynamic_part {269			let part_offset = self.static_part.len() - self.had_call.then(|| 4).unwrap_or(0);270271			let encoded_dynamic_offset = usize::to_be_bytes(part_offset);272			self.static_part[static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len()273				..static_offset + ABI_ALIGNMENT]274				.copy_from_slice(&encoded_dynamic_offset);275			self.static_part.extend(part.finish())276		}277		self.static_part278	}279}280281pub trait AbiRead<T> {282	fn abi_read(&mut self) -> Result<T>;283}284285macro_rules! impl_abi_readable {286	($ty:ty, $method:ident) => {287		impl AbiRead<$ty> for AbiReader<'_> {288			fn abi_read(&mut self) -> Result<$ty> {289				self.$method()290			}291		}292	};293}294295impl_abi_readable!(u8, uint8);296impl_abi_readable!(u32, uint32);297impl_abi_readable!(u64, uint64);298impl_abi_readable!(u128, uint128);299impl_abi_readable!(U256, uint256);300impl_abi_readable!([u8; 4], bytes4);301impl_abi_readable!(H160, address);302impl_abi_readable!(Vec<u8>, bytes);303impl_abi_readable!(bool, bool);304impl_abi_readable!(string, string);305306mod sealed {307	/// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead308	pub trait CanBePlacedInVec {}309}310311impl sealed::CanBePlacedInVec for U256 {}312impl sealed::CanBePlacedInVec for string {}313impl sealed::CanBePlacedInVec for H160 {}314315impl<R: sealed::CanBePlacedInVec> AbiRead<Vec<R>> for AbiReader<'_>316where317	Self: AbiRead<R>,318{319	fn abi_read(&mut self) -> Result<Vec<R>> {320		let mut sub = self.subresult()?;321		let size = sub.read_usize()?;322		sub.subresult_offset = sub.offset;323		let mut out = Vec::with_capacity(size);324		for _ in 0..size {325			out.push(<Self as AbiRead<R>>::abi_read(&mut sub)?);326		}327		Ok(out)328	}329}330331macro_rules! impl_tuples {332	($($ident:ident)+) => {333		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}334		impl<$($ident),+> AbiRead<($($ident,)+)> for AbiReader<'_>335		where336			$(Self: AbiRead<$ident>),+337		{338			fn abi_read(&mut self) -> Result<($($ident,)+)> {339				let mut subresult = self.subresult()?;340				Ok((341					$(<Self as AbiRead<$ident>>::abi_read(&mut subresult)?,)+342				))343			}344		}345		#[allow(non_snake_case)]346		impl<$($ident),+> AbiWrite for &($($ident,)+)347		where348			$($ident: AbiWrite,)+349		{350			fn abi_write(&self, writer: &mut AbiWriter) {351				let ($($ident,)+) = self;352				$($ident.abi_write(writer);)+353			}354		}355	};356}357358impl_tuples! {A}359impl_tuples! {A B}360impl_tuples! {A B C}361impl_tuples! {A B C D}362impl_tuples! {A B C D E}363impl_tuples! {A B C D E F}364impl_tuples! {A B C D E F G}365impl_tuples! {A B C D E F G H}366impl_tuples! {A B C D E F G H I}367impl_tuples! {A B C D E F G H I J}368369pub trait AbiWrite {370	fn abi_write(&self, writer: &mut AbiWriter);371	fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {372		let mut writer = AbiWriter::new();373		self.abi_write(&mut writer);374		Ok(writer.into())375	}376}377378impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {379	// this particular AbiWrite implementation should be split to another trait,380	// which only implements [`to_result`]381	//382	// But due to lack of specialization feature in stable Rust, we can't have383	// blanket impl of this trait `for T where T: AbiWrite`, so here we abusing384	// default trait methods for it385	fn abi_write(&self, _writer: &mut AbiWriter) {386		debug_assert!(false, "shouldn't be called, see comment")387	}388	fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {389		match self {390			Ok(v) => Ok(WithPostDispatchInfo {391				post_info: v.post_info.clone(),392				data: {393					let mut out = AbiWriter::new();394					v.data.abi_write(&mut out);395					out396				},397			}),398			Err(e) => Err(e.clone()),399		}400	}401}402403macro_rules! impl_abi_writeable {404	($ty:ty, $method:ident) => {405		impl AbiWrite for $ty {406			fn abi_write(&self, writer: &mut AbiWriter) {407				writer.$method(&self)408			}409		}410	};411}412413impl_abi_writeable!(u8, uint8);414impl_abi_writeable!(u32, uint32);415impl_abi_writeable!(u128, uint128);416impl_abi_writeable!(U256, uint256);417impl_abi_writeable!(H160, address);418impl_abi_writeable!(bool, bool);419impl_abi_writeable!(&str, string);420impl AbiWrite for &string {421	fn abi_write(&self, writer: &mut AbiWriter) {422		writer.string(self)423	}424}425impl AbiWrite for &Vec<u8> {426	fn abi_write(&self, writer: &mut AbiWriter) {427		writer.bytes(self)428	}429}430431impl AbiWrite for () {432	fn abi_write(&self, _writer: &mut AbiWriter) {}433}434435#[macro_export]436macro_rules! abi_decode {437	($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => {438		$(439			let $name = $reader.$typ()?;440		)+441	}442}443#[macro_export]444macro_rules! abi_encode {445	($($typ:ident($value:expr)),* $(,)?) => {{446		#[allow(unused_mut)]447		let mut writer = ::evm_coder::abi::AbiWriter::new();448		$(449			writer.$typ($value);450		)*451		writer452	}};453	(call $val:expr; $($typ:ident($value:expr)),* $(,)?) => {{454		#[allow(unused_mut)]455		let mut writer = ::evm_coder::abi::AbiWriter::new_call($val);456		$(457			writer.$typ($value);458		)*459		writer460	}}461}462463#[cfg(test)]464pub mod test {465	use crate::{466		abi::AbiRead,467		types::{string, uint256},468	};469470	use super::{AbiReader, AbiWriter};471	use hex_literal::hex;472473	#[test]474	fn dynamic_after_static() {475		let mut encoder = AbiWriter::new();476		encoder.bool(&true);477		encoder.string("test");478		let encoded = encoder.finish();479480		let mut encoder = AbiWriter::new();481		encoder.bool(&true);482		// Offset to subresult483		encoder.uint32(&(32 * 2));484		// Len of "test"485		encoder.uint32(&4);486		encoder.write_padright(&[b't', b'e', b's', b't']);487		let alternative_encoded = encoder.finish();488489		assert_eq!(encoded, alternative_encoded);490491		let mut decoder = AbiReader::new(&encoded);492		assert_eq!(decoder.bool().unwrap(), true);493		assert_eq!(decoder.string().unwrap(), "test");494	}495496	#[test]497	fn mint_sample() {498		let (call, mut decoder) = AbiReader::new_call(&hex!(499			"500				50bb4e7f501				000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374502				0000000000000000000000000000000000000000000000000000000000000001503				0000000000000000000000000000000000000000000000000000000000000060504				0000000000000000000000000000000000000000000000000000000000000008505				5465737420555249000000000000000000000000000000000000000000000000506			"507		))508		.unwrap();509		assert_eq!(call, u32::to_be_bytes(0x50bb4e7f));510		assert_eq!(511			format!("{:?}", decoder.address().unwrap()),512			"0xad2c0954693c2b5404b7e50967d3481bea432374"513		);514		assert_eq!(decoder.uint32().unwrap(), 1);515		assert_eq!(decoder.string().unwrap(), "Test URI");516	}517518	#[test]519	fn mint_bulk() {520		let (call, mut decoder) = AbiReader::new_call(&hex!(521			"522				36543006523				00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address524				0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[]525				0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]526527				0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem528				00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem529				0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem530531				0000000000000000000000000000000000000000000000000000000000000001 // first token id?   					#60532				0000000000000000000000000000000000000000000000000000000000000040 // offset of string533				000000000000000000000000000000000000000000000000000000000000000a // size of string534				5465737420555249203000000000000000000000000000000000000000000000 // string535536				000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11?			#e0537				0000000000000000000000000000000000000000000000000000000000000040 // offset of string538				000000000000000000000000000000000000000000000000000000000000000a // size of string539				5465737420555249203100000000000000000000000000000000000000000000 // string540541				000000000000000000000000000000000000000000000000000000000000000c // third token id?  Why ==12?			#160542				0000000000000000000000000000000000000000000000000000000000000040 // offset of string543				000000000000000000000000000000000000000000000000000000000000000a // size of string544				5465737420555249203200000000000000000000000000000000000000000000 // string545			"546		))547		.unwrap();548		assert_eq!(call, u32::to_be_bytes(0x36543006));549		let _ = decoder.address().unwrap();550		let data =551			<AbiReader<'_> as AbiRead<Vec<(uint256, string)>>>::abi_read(&mut decoder).unwrap();552		assert_eq!(553			data,554			vec![555				(1.into(), "Test URI 0".to_string()),556				(11.into(), "Test URI 1".to_string()),557				(12.into(), "Test URI 2".to_string())558			]559		);560	}561}
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -180,9 +180,16 @@
 			fn is_simple() -> bool {
 				false
 			}
+			#[allow(unused_assignments)]
 			fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
 				write!(writer, "{}(", tc.collect_tuple::<Self>())?;
+				let mut first = true;
 				$(
+					if !first {
+						write!(writer, ",")?;
+					} else {
+						first = false;
+					}
 					<$ident>::solidity_default(writer, tc)?;
 				)*
 				write!(writer, ")")
modifiedpallets/common/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/common/CHANGELOG.md
+++ b/pallets/common/CHANGELOG.md
@@ -2,6 +2,13 @@
 
 All notable changes to this project will be documented in this file.
 
+## [0.1.7] - 2022-08-19
+
+### Added
+
+ - Add convert funtion from `CrossAccountId` to eth `uint256`.
+
+ 
 ## [0.1.6] - 2022-08-16
 
 ### Added
modifiedpallets/common/Cargo.tomldiffbeforeafterboth
--- a/pallets/common/Cargo.toml
+++ b/pallets/common/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "pallet-common"
-version = "0.1.6"
+version = "0.1.7"
 license = "GPLv3"
 edition = "2021"
 
modifiedpallets/common/src/eth.rsdiffbeforeafterboth
--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -16,13 +16,11 @@
 
 //! The module contains a number of functions for converting and checking ethereum identifiers.
 
-use evm_coder::{types::*};
-pub use pallet_evm::account::CrossAccountId;
+use evm_coder::types::uint256;
+pub use pallet_evm::account::{Config, CrossAccountId};
 use sp_core::H160;
 use up_data_structs::CollectionId;
 
-use crate::Config;
-
 // 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection 1
 // TODO: Unhardcode prefix
 const ETH_COLLECTION_PREFIX: [u8; 16] = [
@@ -52,6 +50,15 @@
 	address[0..16] == ETH_COLLECTION_PREFIX
 }
 
+/// Convert `CrossAccountId` to `uint256`.
+pub fn convert_cross_account_to_uint256<T: Config>(from: &T::CrossAccountId) -> uint256
+where
+	T::AccountId: AsRef<[u8; 32]>,
+{
+	let slice = from.as_sub().as_ref();
+	uint256::from_big_endian(slice)
+}
+
 /// Converts Substrate address to CrossAccountId
 pub fn convert_substrate_address_to_cross_account_id<T: Config>(
 	address: uint256,
modifiedpallets/evm-contract-helpers/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/evm-contract-helpers/CHANGELOG.md
+++ b/pallets/evm-contract-helpers/CHANGELOG.md
@@ -1,4 +1,24 @@
-<!-- bureaucrate goes here -->
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+## [v0.2.0] - 2022-08-19
+
+### Added
+
+ - Set arbitrary evm address as contract sponsor.
+ - Ability to remove current sponsor.
+
+### Removed
+ - Remove methods
+   + sponsoring_enabled
+   + toggle_sponsoring
+
+ ### Changed
+
+ - Change `toggle_sponsoring` to `self_sponsored_enable`.
+
+
 ## [v0.1.2] 2022-08-16
 
 ### Other changes
modifiedpallets/evm-contract-helpers/Cargo.tomldiffbeforeafterboth
--- a/pallets/evm-contract-helpers/Cargo.toml
+++ b/pallets/evm-contract-helpers/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "pallet-evm-contract-helpers"
-version = "0.1.2"
+version = "0.2.0"
 license = "GPLv3"
 edition = "2021"
 
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -16,14 +16,16 @@
 
 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_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm};
 use pallet_evm::{
 	ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,
 	account::CrossAccountId,
 };
 use sp_core::H160;
+use up_data_structs::SponsorshipState;
 use crate::{
 	AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT,
+	Sponsoring,
 };
 use frame_support::traits::Get;
 use up_sponsorship::SponsorshipHandler;
@@ -41,36 +43,135 @@
 }
 
 #[solidity_interface(name = "ContractHelpers")]
-impl<T: Config> ContractHelpers<T> {
+impl<T: Config> ContractHelpers<T>
+where
+	T::AccountId: AsRef<[u8; 32]>,
+{
+	/// Get contract ovner
+	///
+	/// @param Contract_address contract for which the owner is being determined.
+	/// @return Contract owner.
 	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(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)
-	}
-
-	/// Deprecated
-	fn toggle_sponsoring(
+	/// Set sponsor.
+	///
+	/// @param contract_address Contract for which a sponsor is being established.
+	/// @param sponsor User address who set as pending sponsor.
+	fn set_sponsor(
 		&mut self,
 		caller: caller,
 		contract_address: address,
-		enabled: bool,
+		sponsor: address,
 	) -> Result<void> {
-		<Pallet<T>>::ensure_owner(contract_address, caller)?;
-		<Pallet<T>>::toggle_sponsoring(contract_address, enabled);
+		self.recorder().consume_sload()?;
+		self.recorder().consume_sstore()?;
+
+		Pallet::<T>::set_sponsor(
+			&T::CrossAccountId::from_eth(caller),
+			contract_address,
+			&T::CrossAccountId::from_eth(sponsor),
+		)
+		.map_err(dispatch_to_evm::<T>)?;
+
+		Ok(())
+	}
+
+	/// Set contract as self sponsored.
+	///
+	/// @param contract_address Contract for which a self sponsoring is being enabled.
+	fn self_sponsored_enable(&mut self, caller: caller, contract_address: address) -> Result<void> {
+		self.recorder().consume_sload()?;
+		self.recorder().consume_sstore()?;
+
+		Pallet::<T>::self_sponsored_enable(&T::CrossAccountId::from_eth(caller), contract_address)
+			.map_err(dispatch_to_evm::<T>)?;
+
+		Ok(())
+	}
+
+	/// Remove sponsor.
+	///
+	/// @param contract_address Contract for which a sponsorship is being removed.
+	fn remove_sponsor(&mut self, caller: caller, contract_address: address) -> Result<void> {
+		self.recorder().consume_sload()?;
+		self.recorder().consume_sstore()?;
+
+		Pallet::<T>::remove_sponsor(&T::CrossAccountId::from_eth(caller), contract_address)
+			.map_err(dispatch_to_evm::<T>)?;
+
+		Ok(())
+	}
+
+	/// Confirm sponsorship.
+	///
+	/// @dev Caller must be same that set via [`set_sponsor`].
+	///
+	/// @param contract_address Сontract for which need to confirm sponsorship.
+	fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {
+		self.recorder().consume_sload()?;
+		self.recorder().consume_sstore()?;
+
+		Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)
+			.map_err(dispatch_to_evm::<T>)?;
+
 		Ok(())
 	}
 
+	/// Get current sponsor.
+	///
+	/// @param contract_address The contract for which a sponsor is requested.
+	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
+	fn get_sponsor(&self, contract_address: address) -> Result<(address, uint256)> {
+		let sponsor =
+			Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;
+		let result: (address, uint256) = if sponsor.is_canonical_substrate() {
+			let sponsor = pallet_common::eth::convert_cross_account_to_uint256::<T>(&sponsor);
+			(Default::default(), sponsor)
+		} else {
+			let sponsor = *sponsor.as_eth();
+			(sponsor, Default::default())
+		};
+		Ok(result)
+	}
+
+	/// Check tat contract has confirmed sponsor.
+	///
+	/// @param contract_address The contract for which the presence of a confirmed sponsor is checked.
+	/// @return **true** if contract has confirmed sponsor.
+	fn has_sponsor(&self, contract_address: address) -> Result<bool> {
+		Ok(Pallet::<T>::get_sponsor(contract_address).is_some())
+	}
+
+	/// Check tat contract has pending sponsor.
+	///
+	/// @param contract_address The contract for which the presence of a pending sponsor is checked.
+	/// @return **true** if contract has pending sponsor.
+	fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {
+		Ok(match Sponsoring::<T>::get(contract_address) {
+			SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,
+			SponsorshipState::Unconfirmed(_) => true,
+		})
+	}
+
+	fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {
+		Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)
+	}
+
 	fn set_sponsoring_mode(
 		&mut self,
 		caller: caller,
 		contract_address: address,
 		mode: uint8,
 	) -> Result<void> {
-		<Pallet<T>>::ensure_owner(contract_address, caller)?;
+		self.recorder().consume_sload()?;
+		self.recorder().consume_sstore()?;
+
+		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;
 		let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;
 		<Pallet<T>>::set_sponsoring_mode(contract_address, mode);
+
 		Ok(())
 	}
 
@@ -84,8 +185,12 @@
 		contract_address: address,
 		rate_limit: uint32,
 	) -> Result<void> {
-		<Pallet<T>>::ensure_owner(contract_address, caller)?;
+		self.recorder().consume_sload()?;
+		self.recorder().consume_sstore()?;
+
+		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;
 		<Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());
+
 		Ok(())
 	}
 
@@ -110,8 +215,12 @@
 		contract_address: address,
 		enabled: bool,
 	) -> Result<void> {
-		<Pallet<T>>::ensure_owner(contract_address, caller)?;
+		self.recorder().consume_sload()?;
+		self.recorder().consume_sstore()?;
+
+		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;
 		<Pallet<T>>::toggle_allowlist(contract_address, enabled);
+
 		Ok(())
 	}
 
@@ -122,14 +231,21 @@
 		user: address,
 		allowed: bool,
 	) -> Result<void> {
-		<Pallet<T>>::ensure_owner(contract_address, caller)?;
+		self.recorder().consume_sload()?;
+		self.recorder().consume_sstore()?;
+
+		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;
 		<Pallet<T>>::toggle_allowed(contract_address, user, allowed);
+
 		Ok(())
 	}
 }
 
 pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);
-impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T> {
+impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>
+where
+	T::AccountId: AsRef<[u8; 32]>,
+{
 	fn is_reserved(contract: &sp_core::H160) -> bool {
 		contract == &T::ContractAddress::get()
 	}
@@ -179,18 +295,26 @@
 	for HelpersContractSponsoring<T>
 {
 	fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {
-		let mode = <Pallet<T>>::sponsoring_mode(call.0);
+		let (contract_address, _) = call;
+		let mode = <Pallet<T>>::sponsoring_mode(*contract_address);
 		if mode == SponsoringModeT::Disabled {
 			return None;
 		}
 
-		if mode == SponsoringModeT::Allowlisted && !<Pallet<T>>::allowed(call.0, *who.as_eth()) {
+		let sponsor = match <Pallet<T>>::get_sponsor(*contract_address) {
+			Some(sponsor) => sponsor,
+			None => return None,
+		};
+
+		if mode == SponsoringModeT::Allowlisted
+			&& !<Pallet<T>>::allowed(*contract_address, *who.as_eth())
+		{
 			return None;
 		}
 		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
 
-		if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who.as_eth()) {
-			let limit = <SponsoringRateLimit<T>>::get(&call.0);
+		if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract_address, who.as_eth()) {
+			let limit = <SponsoringRateLimit<T>>::get(contract_address);
 
 			let timeout = last_tx_block + limit;
 			if block_number < timeout {
@@ -198,9 +322,8 @@
 			}
 		}
 
-		<SponsorBasket<T>>::insert(&call.0, who.as_eth(), block_number);
+		<SponsorBasket<T>>::insert(contract_address, who.as_eth(), block_number);
 
-		let sponsor = T::CrossAccountId::from_eth(call.0);
 		Some(sponsor)
 	}
 }
modifiedpallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -25,9 +25,11 @@
 #[frame_support::pallet]
 pub mod pallet {
 	pub use super::*;
-	use evm_coder::execution::Result;
 	use frame_support::pallet_prelude::*;
+	use pallet_evm_coder_substrate::DispatchResult;
 	use sp_core::H160;
+	use pallet_evm::account::CrossAccountId;
+	use up_data_structs::SponsorshipState;
 
 	#[pallet::config]
 	pub trait Config:
@@ -39,14 +41,21 @@
 
 	#[pallet::error]
 	pub enum Error<T> {
-		/// This method is only executable by owner
+		/// This method is only executable by owner.
 		NoPermission,
+
+		/// No pending sponsor for contract.
+		NoPendingSponsor,
 	}
 
 	#[pallet::pallet]
 	#[pallet::generate_store(pub(super) trait Store)]
 	pub struct Pallet<T>(_);
 
+	/// Store owner for contract.
+	///
+	/// * **Key** - contract address.
+	/// * **Value** - owner for contract.
 	#[pallet::storage]
 	pub(super) type Owner<T: Config> =
 		StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;
@@ -56,10 +65,33 @@
 	pub(super) type SelfSponsoring<T: Config> =
 		StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;
 
+	/// Store for contract sponsorship state.
+	///
+	/// * **Key** - contract address.
+	/// * **Value** - sponsorship state.
+	#[pallet::storage]
+	pub(super) type Sponsoring<T: Config> = StorageMap<
+		Hasher = Twox64Concat,
+		Key = H160,
+		Value = SponsorshipState<T::CrossAccountId>,
+		QueryKind = ValueQuery,
+	>;
+
+	/// Store for sponsoring mode.
+	///
+	/// ### Usage
+	/// Prefer to delete collection from storage if mode chaged to [`Disabled`](SponsoringModeT::Disabled).
+	///
+	/// * **Key** - contract address.
+	/// * **Value** - [`sponsoring mode`](SponsoringModeT).
 	#[pallet::storage]
 	pub(super) type SponsoringMode<T: Config> =
 		StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;
 
+	/// Storage for sponsoring rate limit in blocks.
+	///
+	/// * **Key** - contract address.
+	/// * **Value** - amount of sponsored blocks.
 	#[pallet::storage]
 	pub(super) type SponsoringRateLimit<T: Config> = StorageMap<
 		Hasher = Twox128,
@@ -69,6 +101,11 @@
 		OnEmpty = T::DefaultSponsoringRateLimit,
 	>;
 
+	/// Storage for last sponsored block.
+	///
+	/// * **Key1** - contract address.
+	/// * **Key2** - sponsored user address.
+	/// * **Value** - last sponsored block number.
 	#[pallet::storage]
 	pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<
 		Hasher1 = Twox128,
@@ -79,10 +116,25 @@
 		QueryKind = OptionQuery,
 	>;
 
+	/// Storege for contracts with [`Allowlisted`](SponsoringModeT::Allowlisted) sponsoring mode.
+	///
+	/// ### Usage
+	/// Prefer to delete collection from storage if mode chaged to non `Allowlisted`, than set **Value** to **false**.
+	///
+	/// * **Key** - contract address.
+	/// * **Value** - is contract in [`Allowlisted`](SponsoringModeT::Allowlisted) mode.
 	#[pallet::storage]
 	pub(super) type AllowlistEnabled<T: Config> =
 		StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;
 
+	/// Storage for users that allowed for sponsorship.
+	///
+	/// ### Usage
+	/// Prefer to delete record from storage if user no more allowed for sponsorship.
+	///
+	/// * **Key1** - contract address.
+	/// * **Key2** - user that allowed for sponsorship.
+	/// * **Value** - allowance for sponsorship.
 	#[pallet::storage]
 	pub(super) type Allowlist<T: Config> = StorageDoubleMap<
 		Hasher1 = Twox128,
@@ -94,6 +146,77 @@
 	>;
 
 	impl<T: Config> Pallet<T> {
+		/// Get contract owner.
+		pub fn contract_owner(contract: H160) -> H160 {
+			<Owner<T>>::get(contract)
+		}
+
+		/// Set `sponsor` for `contract`.
+		///
+		/// `sender` must be owner of contract.
+		pub fn set_sponsor(
+			sender: &T::CrossAccountId,
+			contract: H160,
+			sponsor: &T::CrossAccountId,
+		) -> DispatchResult {
+			Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;
+			Sponsoring::<T>::insert(
+				contract,
+				SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),
+			);
+			Ok(())
+		}
+
+		/// Set `contract` as self sponsored.
+		///
+		/// `sender` must be owner of contract.
+		pub fn self_sponsored_enable(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {
+			Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;
+			Sponsoring::<T>::insert(
+				contract,
+				SponsorshipState::<T::CrossAccountId>::Confirmed(T::CrossAccountId::from_eth(
+					contract,
+				)),
+			);
+			Ok(())
+		}
+
+		/// Remove sponsor for `contract`.
+		///
+		/// `sender` must be owner of contract.
+		pub fn remove_sponsor(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {
+			Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;
+			Sponsoring::<T>::remove(contract);
+			Ok(())
+		}
+
+		/// Confirm sponsorship.
+		///
+		/// `sender` must be same that set via [`set_sponsor`].
+		pub fn confirm_sponsorship(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {
+			match Sponsoring::<T>::get(contract) {
+				SponsorshipState::Unconfirmed(sponsor) => {
+					ensure!(sponsor == *sender, Error::<T>::NoPermission);
+					Sponsoring::<T>::insert(
+						contract,
+						SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),
+					);
+					Ok(())
+				}
+				SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {
+					Err(Error::<T>::NoPendingSponsor.into())
+				}
+			}
+		}
+
+		/// Get sponsor.
+		pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {
+			match Sponsoring::<T>::get(contract) {
+				SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,
+				SponsorshipState::Confirmed(sponsor) => Some(sponsor),
+			}
+		}
+
 		pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {
 			<SponsoringMode<T>>::get(contract)
 				.or_else(|| {
@@ -101,6 +224,7 @@
 				})
 				.unwrap_or_default()
 		}
+
 		pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {
 			if mode == SponsoringModeT::Disabled {
 				<SponsoringMode<T>>::remove(contract);
@@ -110,17 +234,6 @@
 			<SelfSponsoring<T>>::remove(contract)
 		}
 
-		pub fn toggle_sponsoring(contract: H160, enabled: bool) {
-			Self::set_sponsoring_mode(
-				contract,
-				if enabled {
-					SponsoringModeT::Allowlisted
-				} else {
-					SponsoringModeT::Disabled
-				},
-			)
-		}
-
 		pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {
 			<SponsoringRateLimit<T>>::insert(contract, rate_limit);
 		}
@@ -137,8 +250,8 @@
 			<Allowlist<T>>::insert(contract, user, allowed);
 		}
 
-		pub fn ensure_owner(contract: H160, user: H160) -> Result<()> {
-			ensure!(<Owner<T>>::get(&contract) == user, "no permission");
+		pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {
+			ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);
 			Ok(())
 		}
 	}
modifiedpallets/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
@@ -3,6 +3,12 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
+// Anonymous struct
+struct Tuple0 {
+	address field_0;
+	uint256 field_1;
+}
+
 // Common stubs holder
 contract Dummy {
 	uint8 dummy;
@@ -21,8 +27,13 @@
 	}
 }
 
-// Selector: 7b4866f9
+// Selector: 6073d917
 contract ContractHelpers is Dummy, ERC165 {
+	// Get contract ovner
+	//
+	// @param Contract_address contract for which the owner is being determined.
+	// @return Contract owner.
+	//
 	// Selector: contractOwner(address) 5152b14c
 	function contractOwner(address contractAddress)
 		public
@@ -35,26 +46,111 @@
 		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Selector: sponsoringEnabled(address) 6027dc61
-	function sponsoringEnabled(address contractAddress)
+	// Set sponsor.
+	//
+	// @param contract_address Contract for which a sponsor is being established.
+	// @param sponsor User address who set as pending sponsor.
+	//
+	// Selector: setSponsor(address,address) f01fba93
+	function setSponsor(address contractAddress, address sponsor) public {
+		require(false, stub_error);
+		contractAddress;
+		sponsor;
+		dummy = 0;
+	}
+
+	// Set contract as self sponsored.
+	//
+	// @param contract_address Contract for which a self sponsoring is being enabled.
+	//
+	// Selector: selfSponsoredEnable(address) 89f7d9ae
+	function selfSponsoredEnable(address contractAddress) public {
+		require(false, stub_error);
+		contractAddress;
+		dummy = 0;
+	}
+
+	// Remove sponsor.
+	//
+	// @param contract_address Contract for which a sponsorship is being removed.
+	//
+	// Selector: removeSponsor(address) ef784250
+	function removeSponsor(address contractAddress) public {
+		require(false, stub_error);
+		contractAddress;
+		dummy = 0;
+	}
+
+	// Confirm sponsorship.
+	//
+	// @dev Caller must be same that set via [`set_sponsor`].
+	//
+	// @param contract_address Сontract for which need to confirm sponsorship.
+	//
+	// Selector: confirmSponsorship(address) abc00001
+	function confirmSponsorship(address contractAddress) public {
+		require(false, stub_error);
+		contractAddress;
+		dummy = 0;
+	}
+
+	// Get current sponsor.
+	//
+	// @param contract_address The contract for which a sponsor is requested.
+	// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
+	//
+	// Selector: getSponsor(address) 743fc745
+	function getSponsor(address contractAddress)
 		public
 		view
-		returns (bool)
+		returns (Tuple0 memory)
 	{
 		require(false, stub_error);
 		contractAddress;
 		dummy;
+		return Tuple0(0x0000000000000000000000000000000000000000, 0);
+	}
+
+	// Check tat contract has confirmed sponsor.
+	//
+	// @param contract_address The contract for which the presence of a confirmed sponsor is checked.
+	// @return **true** if contract has confirmed sponsor.
+	//
+	// Selector: hasSponsor(address) 97418603
+	function hasSponsor(address contractAddress) public view returns (bool) {
+		require(false, stub_error);
+		contractAddress;
+		dummy;
 		return false;
 	}
 
-	// Deprecated
+	// Check tat contract has pending sponsor.
+	//
+	// @param contract_address The contract for which the presence of a pending sponsor is checked.
+	// @return **true** if contract has pending sponsor.
 	//
-	// Selector: toggleSponsoring(address,bool) fcac6d86
-	function toggleSponsoring(address contractAddress, bool enabled) public {
+	// Selector: hasPendingSponsor(address) 39b9b242
+	function hasPendingSponsor(address contractAddress)
+		public
+		view
+		returns (bool)
+	{
+		require(false, stub_error);
+		contractAddress;
+		dummy;
+		return false;
+	}
+
+	// Selector: sponsoringEnabled(address) 6027dc61
+	function sponsoringEnabled(address contractAddress)
+		public
+		view
+		returns (bool)
+	{
 		require(false, stub_error);
 		contractAddress;
-		enabled;
-		dummy = 0;
+		dummy;
+		return false;
 	}
 
 	// Selector: setSponsoringMode(address,uint8) fde8a560
modifiedruntime/tests/src/lib.rsdiffbeforeafterboth
--- a/runtime/tests/src/lib.rs
+++ b/runtime/tests/src/lib.rs
@@ -167,7 +167,7 @@
 }
 
 #[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, TypeInfo, MaxEncodedLen)]
-pub struct TestCrossAccountId(u64, sp_core::H160);
+pub struct TestCrossAccountId(u64, sp_core::H160, bool);
 impl CrossAccountId<u64> for TestCrossAccountId {
 	fn as_sub(&self) -> &u64 {
 		&self.0
@@ -178,17 +178,20 @@
 	fn from_sub(sub: u64) -> Self {
 		let mut eth = [0; 20];
 		eth[12..20].copy_from_slice(&sub.to_be_bytes());
-		Self(sub, sp_core::H160(eth))
+		Self(sub, sp_core::H160(eth), true)
 	}
 	fn from_eth(eth: sp_core::H160) -> Self {
 		let mut sub_raw = [0; 8];
 		sub_raw.copy_from_slice(&eth.0[0..8]);
 		let sub = u64::from_be_bytes(sub_raw);
-		Self(sub, eth)
+		Self(sub, eth, false)
 	}
 	fn conv_eq(&self, other: &Self) -> bool {
 		self.as_sub() == other.as_sub()
 	}
+	fn is_canonical_substrate(&self) -> bool {
+		self.2
+	}
 }
 
 impl Default for TestCrossAccountId {
modifiedtests/src/eth/api/ContractHelpers.soldiffbeforeafterboth
--- a/tests/src/eth/api/ContractHelpers.sol
+++ b/tests/src/eth/api/ContractHelpers.sol
@@ -3,6 +3,12 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
+// Anonymous struct
+struct Tuple0 {
+	address field_0;
+	uint256 field_1;
+}
+
 // Common stubs holder
 interface Dummy {
 
@@ -12,24 +18,85 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
-// Selector: 7b4866f9
+// Selector: 6073d917
 interface ContractHelpers is Dummy, ERC165 {
+	// Get contract ovner
+	//
+	// @param Contract_address contract for which the owner is being determined.
+	// @return Contract owner.
+	//
 	// Selector: contractOwner(address) 5152b14c
 	function contractOwner(address contractAddress)
 		external
 		view
 		returns (address);
 
+	// Set sponsor.
+	//
+	// @param contract_address Contract for which a sponsor is being established.
+	// @param sponsor User address who set as pending sponsor.
+	//
+	// Selector: setSponsor(address,address) f01fba93
+	function setSponsor(address contractAddress, address sponsor) external;
+
+	// Set contract as self sponsored.
+	//
+	// @param contract_address Contract for which a self sponsoring is being enabled.
+	//
+	// Selector: selfSponsoredEnable(address) 89f7d9ae
+	function selfSponsoredEnable(address contractAddress) external;
+
+	// Remove sponsor.
+	//
+	// @param contract_address Contract for which a sponsorship is being removed.
+	//
+	// Selector: removeSponsor(address) ef784250
+	function removeSponsor(address contractAddress) external;
+
+	// Confirm sponsorship.
+	//
+	// @dev Caller must be same that set via [`set_sponsor`].
+	//
+	// @param contract_address Сontract for which need to confirm sponsorship.
+	//
+	// Selector: confirmSponsorship(address) abc00001
+	function confirmSponsorship(address contractAddress) external;
+
+	// Get current sponsor.
+	//
+	// @param contract_address The contract for which a sponsor is requested.
+	// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
+	//
+	// Selector: getSponsor(address) 743fc745
+	function getSponsor(address contractAddress)
+		external
+		view
+		returns (Tuple0 memory);
+
+	// Check tat contract has confirmed sponsor.
+	//
+	// @param contract_address The contract for which the presence of a confirmed sponsor is checked.
+	// @return **true** if contract has confirmed sponsor.
+	//
+	// Selector: hasSponsor(address) 97418603
+	function hasSponsor(address contractAddress) external view returns (bool);
+
+	// Check tat contract has pending sponsor.
+	//
+	// @param contract_address The contract for which the presence of a pending sponsor is checked.
+	// @return **true** if contract has pending sponsor.
+	//
+	// Selector: hasPendingSponsor(address) 39b9b242
+	function hasPendingSponsor(address contractAddress)
+		external
+		view
+		returns (bool);
+
 	// Selector: sponsoringEnabled(address) 6027dc61
 	function sponsoringEnabled(address contractAddress)
 		external
 		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;
modifiedtests/src/eth/collectionSponsoring.test.tsdiffbeforeafterboth
--- a/tests/src/eth/collectionSponsoring.test.ts
+++ b/tests/src/eth/collectionSponsoring.test.ts
@@ -1,7 +1,8 @@
-import {addToAllowListExpectSuccess, confirmSponsorshipExpectSuccess, createCollectionExpectSuccess, enablePublicMintingExpectSuccess, setCollectionSponsorExpectSuccess} from '../util/helpers';
-import {itWeb3, createEthAccount, collectionIdToAddress, GAS_ARGS, normalizeEvents} from './util/helpers';
+import {addToAllowListExpectSuccess, confirmSponsorshipExpectSuccess, createCollectionExpectSuccess, enablePublicMintingExpectSuccess, getDetailedCollectionInfo, setCollectionSponsorExpectSuccess} from '../util/helpers';
+import {itWeb3, createEthAccount, collectionIdToAddress, GAS_ARGS, normalizeEvents, createEthAccountWithBalance, evmCollectionHelpers, getCollectionAddressFromResult, evmCollection, ethBalanceViaSub} from './util/helpers';
 import nonFungibleAbi from './nonFungibleAbi.json';
 import {expect} from 'chai';
+import {evmToAddress} from '@polkadot/util-crypto';
 
 describe('evm collection sponsoring', () => {
   itWeb3('sponsors mint transactions', async ({web3, privateKeyWrapper}) => {
@@ -36,4 +37,130 @@
       },
     ]);
   });
+
+  itWeb3('Sponsoring collection from evm address via access list', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelpers = evmCollectionHelpers(web3, owner);
+    let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    result = await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner});
+    let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+    const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
+    expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
+    expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
+    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
+
+    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+    collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+    expect(collectionSub.sponsorship.isConfirmed).to.be.true;
+    expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
+
+    const user = createEthAccount(web3);
+    const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+    expect(nextTokenId).to.be.equal('1');
+
+    const oldPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
+    expect(oldPermissions.mintMode).to.be.false;
+    expect(oldPermissions.access).to.be.equal('Normal');
+
+    await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
+    await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
+    await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
+
+    const newPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
+    expect(newPermissions.mintMode).to.be.true;
+    expect(newPermissions.access).to.be.equal('AllowList');
+
+    const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
+    const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
+
+    {
+      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await collectionEvm.methods.mintWithTokenURI(
+        user,
+        nextTokenId,
+        'Test URI',
+      ).send({from: user});
+      const events = normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address: collectionIdAddress,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: user,
+            tokenId: nextTokenId,
+          },
+        },
+      ]);
+
+      const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
+      const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);
+
+      expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+      expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+      expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
+    }
+  });
+
+  itWeb3('Check that transaction via EVM spend money from sponsor address', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelpers = evmCollectionHelpers(web3, owner);
+    let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();
+    let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+    const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
+    expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
+    expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
+    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
+    const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
+    await sponsorCollection.methods.confirmCollectionSponsorship().send();
+    collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+    expect(collectionSub.sponsorship.isConfirmed).to.be.true;
+    expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
+
+    const user = createEthAccount(web3);
+    await collectionEvm.methods.addCollectionAdmin(user).send();
+    
+    const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
+    const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
+    
+  
+    const userCollectionEvm = evmCollection(web3, user, collectionIdAddress);
+    const nextTokenId = await userCollectionEvm.methods.nextTokenId().call();
+    expect(nextTokenId).to.be.equal('1');
+    result = await userCollectionEvm.methods.mintWithTokenURI(
+      user,
+      nextTokenId,
+      'Test URI',
+    ).send();
+
+    const events = normalizeEvents(result.events);
+    const address = collectionIdToAddress(collectionId);
+
+    expect(events).to.be.deep.equal([
+      {
+        address,
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: user,
+          tokenId: nextTokenId,
+        },
+      },
+    ]);
+    expect(await userCollectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+  
+    const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
+    expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+    const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);
+    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+  });
 });
modifiedtests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth
--- a/tests/src/eth/contractSponsoring.test.ts
+++ b/tests/src/eth/contractSponsoring.test.ts
@@ -23,33 +23,35 @@
   itWeb3,
   SponsoringMode,
   createEthAccount,
-  collectionIdToAddress,
-  GAS_ARGS,
-  normalizeEvents,
-  subToEth,
-  executeEthTxOnSub,
-  evmCollectionHelpers,
-  getCollectionAddressFromResult,
-  evmCollection,
   ethBalanceViaSub,
 } from './util/helpers';
-import {
-  addCollectionAdminExpectSuccess,
-  createCollectionExpectSuccess,
-  getDetailedCollectionInfo,
-  transferBalanceTo,
-} from '../util/helpers';
-import nonFungibleAbi from './nonFungibleAbi.json';
-import getBalance from '../substrate/get-balance';
-import {evmToAddress} from '@polkadot/util-crypto';
 
 describe('Sponsoring EVM contracts', () => {
+  itWeb3('Self sponsored can be set by the address that deployed the contract', 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.hasSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected;
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+  });
+
+  itWeb3('Self sponsored can not be set by the address that did not deployed the contract', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const notOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const flipper = await deployFlipper(web3, owner);
+    const helpers = contractHelpers(web3, owner);
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+  });
+
   itWeb3('Sponsoring can be set by the address that has deployed the contract', 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.sponsoringEnabled(flipper.options.address).call()).to.be.false;
-    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
+    await expect(helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner})).to.be.not.rejected;
     expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
   });
 
@@ -59,11 +61,150 @@
     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.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).send({from: notOwner})).to.rejected;
+    await expect(helpers.methods.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).call({from: notOwner})).to.be.rejectedWith('NoPermission');
     expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
   });
+  
+  itWeb3('Sponsor can be set by the address that deployed the contract', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const flipper = await deployFlipper(web3, owner);
+    const helpers = contractHelpers(web3, owner);
+    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;
+    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true;
+  });
+  
+  itWeb3('Sponsor can not be set by the address that did not deployed the contract', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const notOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const flipper = await deployFlipper(web3, owner);
+    const helpers = contractHelpers(web3, owner);
+    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
+  });
+
+  itWeb3('Sponsorship can be confirmed by the address that pending as sponsor', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const flipper = await deployFlipper(web3, owner);
+    const helpers = contractHelpers(web3, owner);
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;
+    await expect(helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor})).to.be.not.rejected;
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+  });
+
+  itWeb3('Sponsorship can not be confirmed by the address that not pending as sponsor', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const notSponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const flipper = await deployFlipper(web3, owner);
+    const helpers = contractHelpers(web3, owner);
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;
+    await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPermission');
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+  });
+
+  itWeb3('Sponsorship can not be confirmed by the address that not set as sponsor', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const notSponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const flipper = await deployFlipper(web3, owner);
+    const helpers = contractHelpers(web3, owner);
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPendingSponsor');
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+  });
+
+  itWeb3('Get self sponsored sponsor', 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.selfSponsoredEnable(flipper.options.address).send();
+    
+    const result = await helpers.methods.getSponsor(flipper.options.address).call();
+
+    expect(result[0]).to.be.eq(flipper.options.address);
+    expect(result[1]).to.be.eq('0');
+  });
+
+  itWeb3('Get confirmed sponsor', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const flipper = await deployFlipper(web3, owner);
+    const helpers = contractHelpers(web3, owner);
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+    
+    const result = await helpers.methods.getSponsor(flipper.options.address).call();
 
+    expect(result[0]).to.be.eq(sponsor);
+    expect(result[1]).to.be.eq('0');
+  });
+
+  itWeb3('Sponsor can be removed by the address that deployed the contract', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const flipper = await deployFlipper(web3, owner);
+    const helpers = contractHelpers(web3, owner);
+
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+    
+    await helpers.methods.removeSponsor(flipper.options.address).send();
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+  });
+
+  itWeb3('Sponsor can not be removed by the address that did not deployed the contract', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const notOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const flipper = await deployFlipper(web3, owner);
+    const helpers = contractHelpers(web3, owner);
+
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+    
+    await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+  });
+
   itWeb3('In generous mode, non-allowlisted user transaction will be sponsored', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+
+    const flipper = await deployFlipper(web3, owner);
+
+    const helpers = contractHelpers(web3, owner);
+
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+
+    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});
+    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+
+    const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
+    const callerBalanceBefore = await ethBalanceViaSub(api, caller);
+
+    await flipper.methods.flip().send({from: caller});
+    expect(await flipper.methods.getValue().call()).to.be.true;
+
+    // Balance should be taken from sponsor instead of caller
+    const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);
+    const callerBalanceAfter = await ethBalanceViaSub(api, caller);
+    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+    expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);
+  });
+
+  itWeb3('In generous mode, non-allowlisted user transaction will be self sponsored', async ({api, web3, privateKeyWrapper}) => {
     const alice = privateKeyWrapper('//Alice');
 
     const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
@@ -73,28 +214,29 @@
 
     const helpers = contractHelpers(web3, owner);
 
-    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
+    await helpers.methods.selfSponsoredEnable(flipper.options.address).send();
+
     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');
+    const contractBalanceBefore = await ethBalanceViaSub(api, flipper.options.address);
+    const callerBalanceBefore = await ethBalanceViaSub(api, caller);
 
     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);
+    // Balance should be taken from sponsor instead of caller
+    const contractBalanceAfter = await ethBalanceViaSub(api, flipper.options.address);
+    const callerBalanceAfter = await ethBalanceViaSub(api, caller);
+    expect(contractBalanceAfter < contractBalanceBefore).to.be.true;
+    expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);
   });
 
   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, privateKeyWrapper}) => {
-    const alice = privateKeyWrapper('//Alice');
-
     const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const caller = createEthAccount(web3);
 
     const flipper = await deployFlipper(web3, owner);
@@ -103,22 +245,21 @@
     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.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;
 
-    await transferBalanceToEth(api, alice, flipper.options.address);
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
 
-    const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);
-    expect(originalFlipperBalance).to.be.not.equal('0');
+    const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
+    expect(sponsorBalanceBefore).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);
+    const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);
+    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
   });
 
   itWeb3('Sponsoring is set, an address that has no UNQ can send a transaction and it works. Sponsor balance should not decrease (non-allowlisted)', async ({api, web3, privateKeyWrapper}) => {
@@ -131,10 +272,8 @@
 
     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.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;
 
     await transferBalanceToEth(api, alice, flipper.options.address);
 
@@ -150,11 +289,9 @@
   });
 
   itWeb3('Sponsoring is set, an address that has UNQ can send a transaction and it works. User balance should not change', async ({api, web3, privateKeyWrapper}) => {
-    const alice = privateKeyWrapper('//Alice');
-
     const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const originalCallerBalance = await web3.eth.getBalance(caller);
 
     const flipper = await deployFlipper(web3, owner);
 
@@ -162,26 +299,27 @@
     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.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;
 
-    await transferBalanceToEth(api, alice, flipper.options.address);
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
 
-    const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);
-    expect(originalFlipperBalance).to.be.not.equal('0');
+    const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
+    const callerBalanceBefore = await ethBalanceViaSub(api, caller);
 
     await flipper.methods.flip().send({from: caller});
     expect(await flipper.methods.getValue().call()).to.be.true;
 
-    expect(await web3.eth.getBalance(caller)).to.be.equals(originalCallerBalance);
+    const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);
+    const callerBalanceAfter = await ethBalanceViaSub(api, caller);
+    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+    expect(callerBalanceAfter).to.be.equals(callerBalanceBefore);
   });
 
   itWeb3('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({api, web3, privateKeyWrapper}) => {
-    const alice = privateKeyWrapper('//Alice');
-
     const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const originalCallerBalance = await web3.eth.getBalance(caller);
 
@@ -191,25 +329,24 @@
     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.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;
 
-    await transferBalanceToEth(api, alice, flipper.options.address);
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
 
-    const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);
+    const originalFlipperBalance = await web3.eth.getBalance(sponsor);
     expect(originalFlipperBalance).to.be.not.equal('0');
 
     await flipper.methods.flip().send({from: caller});
     expect(await flipper.methods.getValue().call()).to.be.true;
     expect(await web3.eth.getBalance(caller)).to.be.equals(originalCallerBalance);
 
-    const newFlipperBalance = await web3.eth.getBalance(flipper.options.address);
+    const newFlipperBalance = await web3.eth.getBalance(sponsor);
     expect(newFlipperBalance).to.be.not.equals(originalFlipperBalance);
 
     await flipper.methods.flip().send({from: caller});
-    expect(await web3.eth.getBalance(flipper.options.address)).to.be.equal(newFlipperBalance);
+    expect(await web3.eth.getBalance(sponsor)).to.be.equal(newFlipperBalance);
     expect(await web3.eth.getBalance(caller)).to.be.not.equals(originalCallerBalance);
   });
 
@@ -219,131 +356,5 @@
     const flipper = await deployFlipper(web3, owner);
     const helpers = contractHelpers(web3, owner);
     expect(await helpers.methods.getSponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200');
-  });
-
-  itWeb3('Sponsoring collection from evm address via access list', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const collectionHelpers = evmCollectionHelpers(web3, owner);
-    let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
-    result = await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner});
-    let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
-    const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
-    expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
-    expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
-    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
-
-    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-    collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
-    expect(collectionSub.sponsorship.isConfirmed).to.be.true;
-    expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
-
-    const user = createEthAccount(web3);
-    const nextTokenId = await collectionEvm.methods.nextTokenId().call();
-    expect(nextTokenId).to.be.equal('1');
-
-    const oldPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
-    expect(oldPermissions.mintMode).to.be.false;
-    expect(oldPermissions.access).to.be.equal('Normal');
-
-    await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
-    await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
-    await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
-
-    const newPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
-    expect(newPermissions.mintMode).to.be.true;
-    expect(newPermissions.access).to.be.equal('AllowList');
-
-    const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
-    const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
-
-    {
-      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
-      expect(nextTokenId).to.be.equal('1');
-      const result = await collectionEvm.methods.mintWithTokenURI(
-        user,
-        nextTokenId,
-        'Test URI',
-      ).send({from: user});
-      const events = normalizeEvents(result.events);
-
-      expect(events).to.be.deep.equal([
-        {
-          address: collectionIdAddress,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: user,
-            tokenId: nextTokenId,
-          },
-        },
-      ]);
-
-      const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
-      const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);
-
-      expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
-      expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
-      expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
-    }
-  });
-
-  itWeb3('Check that transaction via EVM spend money from sponsor address', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const collectionHelpers = evmCollectionHelpers(web3, owner);
-    let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
-    result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();
-    let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
-    const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
-    expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
-    expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
-    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
-    const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
-    await sponsorCollection.methods.confirmCollectionSponsorship().send();
-    collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
-    expect(collectionSub.sponsorship.isConfirmed).to.be.true;
-    expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
-
-    const user = createEthAccount(web3);
-    await collectionEvm.methods.addCollectionAdmin(user).send();
-    
-    const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
-    const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
-    
-  
-    const userCollectionEvm = evmCollection(web3, user, collectionIdAddress);
-    const nextTokenId = await userCollectionEvm.methods.nextTokenId().call();
-    expect(nextTokenId).to.be.equal('1');
-    result = await userCollectionEvm.methods.mintWithTokenURI(
-      user,
-      nextTokenId,
-      'Test URI',
-    ).send();
-
-    const events = normalizeEvents(result.events);
-    const address = collectionIdToAddress(collectionId);
-
-    expect(events).to.be.deep.equal([
-      {
-        address,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: user,
-          tokenId: nextTokenId,
-        },
-      },
-    ]);
-    expect(await userCollectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
-  
-    const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
-    expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-    const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);
-    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
   });
 });
modifiedtests/src/eth/marketplace/marketplace.test.tsdiffbeforeafterboth
--- a/tests/src/eth/marketplace/marketplace.test.ts
+++ b/tests/src/eth/marketplace/marketplace.test.ts
@@ -39,6 +39,7 @@
 describe('Matcher contract usage', () => {
   itWeb3('With UNQ', async ({api, web3, privateKeyWrapper}) => {
     const alice = privateKeyWrapper('//Alice');
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const matcherOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
       from: matcherOwner,
@@ -48,7 +49,9 @@
     const helpers = contractHelpers(web3, 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);
+    
+    await helpers.methods.setSponsor(matcher.options.address, sponsor).send({from: matcherOwner});
+    await helpers.methods.confirmSponsorship(matcher.options.address).send({from: sponsor});
 
     const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
     await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorApproveTimeout: 1});
@@ -100,6 +103,7 @@
 
   itWeb3('With escrow', async ({api, web3, privateKeyWrapper}) => {
     const alice = privateKeyWrapper('//Alice');
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const matcherOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const escrow = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
@@ -111,7 +115,9 @@
     const helpers = contractHelpers(web3, 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);
+    
+    await helpers.methods.setSponsor(matcher.options.address, sponsor).send({from: matcherOwner});
+    await helpers.methods.confirmSponsorship(matcher.options.address).send({from: sponsor});
 
     const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
     await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorApproveTimeout: 1});
modifiedtests/src/eth/sponsoring.test.tsdiffbeforeafterboth
--- a/tests/src/eth/sponsoring.test.ts
+++ b/tests/src/eth/sponsoring.test.ts
@@ -15,13 +15,12 @@
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
 import {expect} from 'chai';
-import {contractHelpers, createEthAccount, createEthAccountWithBalance, deployCollector, deployFlipper, itWeb3, SponsoringMode, transferBalanceToEth} from './util/helpers';
+import {contractHelpers, createEthAccount, createEthAccountWithBalance, deployCollector, deployFlipper, itWeb3, SponsoringMode} from './util/helpers';
 
 describe('EVM sponsoring', () => {
   itWeb3('Fee is deducted from contract if sponsoring is enabled', async ({api, web3, privateKeyWrapper}) => {
-    const alice = privateKeyWrapper('//Alice');
-
     const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const caller = createEthAccount(web3);
     const originalCallerBalance = await web3.eth.getBalance(caller);
     expect(originalCallerBalance).to.be.equal('0');
@@ -31,28 +30,29 @@
     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});
+    
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send({from: owner});
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
 
     expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
     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;
 
-    await transferBalanceToEth(api, alice, flipper.options.address);
-
-    const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);
-    expect(originalFlipperBalance).to.be.not.equal('0');
+    const originalSponsorBalance = await web3.eth.getBalance(sponsor);
+    expect(originalSponsorBalance).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
     expect(await web3.eth.getBalance(caller)).to.be.equals(originalCallerBalance);
-    expect(await web3.eth.getBalance(flipper.options.address)).to.be.not.equals(originalFlipperBalance);
+    expect(await web3.eth.getBalance(sponsor)).to.be.not.equals(originalSponsorBalance);
   });
+
   itWeb3('...but this doesn\'t applies to payable value', async ({api, web3, privateKeyWrapper}) => {
-    const alice = privateKeyWrapper('//Alice');
-
     const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const originalCallerBalance = await web3.eth.getBalance(caller);
     expect(originalCallerBalance).to.be.not.equal('0');
@@ -68,16 +68,17 @@
     await helpers.methods.setSponsoringRateLimit(collector.options.address, 0).send({from: owner});
     expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.true;
 
-    await transferBalanceToEth(api, alice, collector.options.address);
+    await helpers.methods.setSponsor(collector.options.address, sponsor).send({from: owner});
+    await helpers.methods.confirmSponsorship(collector.options.address).send({from: sponsor});
 
-    const originalCollectorBalance = await web3.eth.getBalance(collector.options.address);
-    expect(originalCollectorBalance).to.be.not.equal('0');
+    const originalSponsorBalance = await web3.eth.getBalance(sponsor);
+    expect(originalSponsorBalance).to.be.not.equal('0');
 
     await collector.methods.giveMoney().send({from: caller, value: '10000'});
 
     // Balance will be taken from both caller (value) and from collector (fee)
     expect(await web3.eth.getBalance(caller)).to.be.equals((BigInt(originalCallerBalance) - 10000n).toString());
-    expect(await web3.eth.getBalance(collector.options.address)).to.be.not.equals(originalCollectorBalance);
+    expect(await web3.eth.getBalance(sponsor)).to.be.not.equals(originalSponsorBalance);
     expect(await collector.methods.getCollected().call()).to.be.equal('10000');
   });
 });
modifiedtests/src/eth/util/contractHelpersAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/util/contractHelpersAbi.json
+++ b/tests/src/eth/util/contractHelpersAbi.json
@@ -34,6 +34,19 @@
         "type": "address"
       }
     ],
+    "name": "confirmSponsorship",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
     "name": "contractOwner",
     "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
     "stateMutability": "view",
@@ -47,6 +60,29 @@
         "type": "address"
       }
     ],
+    "name": "getSponsor",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "field_0", "type": "address" },
+          { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+        ],
+        "internalType": "struct Tuple0",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
     "name": "getSponsoringRateLimit",
     "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }],
     "stateMutability": "view",
@@ -58,6 +94,72 @@
         "internalType": "address",
         "name": "contractAddress",
         "type": "address"
+      }
+    ],
+    "name": "hasPendingSponsor",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "hasSponsor",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "removeSponsor",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "selfSponsoredEnable",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "address", "name": "sponsor", "type": "address" }
+    ],
+    "name": "setSponsor",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
       },
       { "internalType": "uint8", "name": "mode", "type": "uint8" }
     ],
@@ -140,20 +242,6 @@
       { "internalType": "bool", "name": "enabled", "type": "bool" }
     ],
     "name": "toggleAllowlist",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      },
-      { "internalType": "bool", "name": "enabled", "type": "bool" }
-    ],
-    "name": "toggleSponsoring",
     "outputs": [],
     "stateMutability": "nonpayable",
     "type": "function"