difftreelog
Merge branch 'feature/app-staking' of https://github.com/UniqueNetwork/unique-chain into feature/app-staking
in: master
11 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5735,6 +5735,7 @@
name = "pallet-evm-contract-helpers"
version = "0.2.0"
dependencies = [
+ "ethereum",
"evm-coder",
"fp-evm-mapping",
"frame-support",
pallets/evm-contract-helpers/Cargo.tomldiffbeforeafterboth--- a/pallets/evm-contract-helpers/Cargo.toml
+++ b/pallets/evm-contract-helpers/Cargo.toml
@@ -9,6 +9,7 @@
"derive",
] }
log = { default-features = false, version = "0.4.14" }
+ethereum = { version = "0.12.0", default-features = false }
# Substrate
frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" }
pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -17,7 +17,9 @@
//! Implementation of magic contract
use core::marker::PhantomData;
-use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};
+use evm_coder::{
+ abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog,
+};
use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm};
use pallet_evm::{
ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,
@@ -33,6 +35,35 @@
use up_sponsorship::SponsorshipHandler;
use sp_std::vec::Vec;
+/// Pallet events.
+#[derive(ToLog)]
+pub enum ContractHelpersEvents {
+ /// Contract sponsor was set.
+ ContractSponsorSet {
+ /// Contract address of the affected collection.
+ #[indexed]
+ contract_address: address,
+ /// New sponsor address.
+ sponsor: address,
+ },
+
+ /// New sponsor was confirm.
+ ContractSponsorshipConfirmed {
+ /// Contract address of the affected collection.
+ #[indexed]
+ contract_address: address,
+ /// New sponsor address.
+ sponsor: address,
+ },
+
+ /// Collection sponsor was removed.
+ ContractSponsorRemoved {
+ /// Contract address of the affected collection.
+ #[indexed]
+ contract_address: address,
+ },
+}
+
/// See [`ContractHelpersCall`]
pub struct ContractHelpers<T: Config>(SubstrateRecorder<T>);
impl<T: Config> WithRecorder<T> for ContractHelpers<T> {
@@ -46,7 +77,7 @@
}
/// @title Magic contract, which allows users to reconfigure other contracts
-#[solidity_interface(name = ContractHelpers)]
+#[solidity_interface(name = ContractHelpers, events(ContractHelpersEvents))]
impl<T: Config> ContractHelpers<T>
where
T::AccountId: AsRef<[u8; 32]>,
@@ -91,8 +122,12 @@
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>)?;
+ Pallet::<T>::force_set_sponsor(
+ &T::CrossAccountId::from_eth(caller),
+ contract_address,
+ &T::CrossAccountId::from_eth(contract_address),
+ )
+ .map_err(dispatch_to_evm::<T>)?;
Ok(())
}
pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -16,7 +16,7 @@
#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
-#![deny(missing_docs)]
+#![warn(missing_docs)]
use codec::{Decode, Encode, MaxEncodedLen};
pub use pallet::*;
@@ -27,18 +27,24 @@
#[frame_support::pallet]
pub mod pallet {
pub use super::*;
+ use crate::eth::ContractHelpersEvents;
use frame_support::pallet_prelude::*;
use pallet_evm_coder_substrate::DispatchResult;
use sp_core::H160;
- use pallet_evm::account::CrossAccountId;
+ use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
use up_data_structs::SponsorshipState;
+ use evm_coder::ToLog;
#[pallet::config]
pub trait Config:
frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config
{
+ /// Overarching event type.
+ type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;
+
/// Address, under which magic contract will be available
type ContractAddress: Get<H160>;
+
/// In case of enabled sponsoring, but no sponsoring rate limit set,
/// this value will be used implicitly
type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;
@@ -150,6 +156,32 @@
QueryKind = ValueQuery,
>;
+ #[pallet::event]
+ #[pallet::generate_deposit(pub fn deposit_event)]
+ pub enum Event<T: Config> {
+ /// Contract sponsor was set.
+ ContractSponsorSet(
+ /// Contract address of the affected collection.
+ H160,
+ /// New sponsor address.
+ T::AccountId,
+ ),
+
+ /// New sponsor was confirm.
+ ContractSponsorshipConfirmed(
+ /// Contract address of the affected collection.
+ H160,
+ /// New sponsor address.
+ T::AccountId,
+ ),
+
+ /// Collection sponsor was removed.
+ ContractSponsorRemoved(
+ /// Contract address of the affected collection.
+ H160,
+ ),
+ }
+
impl<T: Config> Pallet<T> {
/// Get contract owner.
pub fn contract_owner(contract: H160) -> H160 {
@@ -169,43 +201,108 @@
contract,
SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),
);
+
+ <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(
+ contract,
+ sponsor.as_sub().clone(),
+ ));
+ <PalletEvm<T>>::deposit_log(
+ ContractHelpersEvents::ContractSponsorSet {
+ contract_address: contract,
+ sponsor: *sponsor.as_eth(),
+ }
+ .to_log(contract),
+ );
Ok(())
}
- /// Set `contract` as self sponsored.
+ /// Set sponsor as already confirmed.
///
/// `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())?;
+ pub fn force_set_sponsor(
+ sender: &T::CrossAccountId,
+ contract_address: H160,
+ sponsor: &T::CrossAccountId,
+ ) -> DispatchResult {
+ Pallet::<T>::ensure_owner(contract_address, *sender.as_eth())?;
Sponsoring::<T>::insert(
- contract,
+ contract_address,
SponsorshipState::<T::CrossAccountId>::Confirmed(T::CrossAccountId::from_eth(
- contract,
+ contract_address,
)),
);
+
+ let eth_sponsor = *sponsor.as_eth();
+ let sub_sponsor = sponsor.as_sub().clone();
+
+ <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(
+ contract_address,
+ sub_sponsor.clone(),
+ ));
+ <PalletEvm<T>>::deposit_log(
+ ContractHelpersEvents::ContractSponsorSet {
+ contract_address,
+ sponsor: eth_sponsor,
+ }
+ .to_log(contract_address),
+ );
+
+ <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(
+ contract_address,
+ sub_sponsor,
+ ));
+ <PalletEvm<T>>::deposit_log(
+ ContractHelpersEvents::ContractSponsorshipConfirmed {
+ contract_address,
+ sponsor: eth_sponsor,
+ }
+ .to_log(contract_address),
+ );
+
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);
+ pub fn remove_sponsor(sender: &T::CrossAccountId, contract_address: H160) -> DispatchResult {
+ Pallet::<T>::ensure_owner(contract_address, *sender.as_eth())?;
+ Sponsoring::<T>::remove(contract_address);
+
+ <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));
+ <PalletEvm<T>>::deposit_log(
+ ContractHelpersEvents::ContractSponsorRemoved { contract_address }.to_log(contract_address),
+ );
+
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) {
+ pub fn confirm_sponsorship(sender: &T::CrossAccountId, contract_address: H160) -> DispatchResult {
+ match Sponsoring::<T>::get(contract_address) {
SponsorshipState::Unconfirmed(sponsor) => {
ensure!(sponsor == *sender, Error::<T>::NoPermission);
+ let eth_sponsor = *sponsor.as_eth();
+ let sub_sponsor = sponsor.as_sub().clone();
Sponsoring::<T>::insert(
- contract,
+ contract_address,
SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),
);
+
+ <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(
+ contract_address,
+ sub_sponsor,
+ ));
+ <PalletEvm<T>>::deposit_log(
+ ContractHelpersEvents::ContractSponsorshipConfirmed {
+ contract_address,
+ sponsor: eth_sponsor,
+ }
+ .to_log(contract_address),
+ );
+
Ok(())
}
SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {
pallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/evm-contract-helpers/src/stubs/ContractHelpers.soldiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
+++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
@@ -21,9 +21,19 @@
}
}
+/// @dev inlined interface
+contract ContractHelpersEvents {
+ event ContractSponsorSet(address indexed contractAddress, address sponsor);
+ event ContractSponsorshipConfirmed(
+ address indexed contractAddress,
+ address sponsor
+ );
+ event ContractSponsorRemoved(address indexed contractAddress);
+}
+
/// @title Magic contract, which allows users to reconfigure other contracts
/// @dev the ERC-165 identifier for this interface is 0xd77fab70
-contract ContractHelpers is Dummy, ERC165 {
+contract ContractHelpers is Dummy, ERC165, ContractHelpersEvents {
/// Get user, which deployed specified contract
/// @dev May return zero address in case if contract is deployed
/// using uniquenetwork evm-migration pallet, or using other terms not
runtime/common/config/ethereum.rsdiffbeforeafterboth--- a/runtime/common/config/ethereum.rs
+++ b/runtime/common/config/ethereum.rs
@@ -112,6 +112,7 @@
}
impl pallet_evm_contract_helpers::Config for Runtime {
+ type Event = Event;
type ContractAddress = HelpersContractAddress;
type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;
}
runtime/common/construct_runtime/mod.rsdiffbeforeafterboth--- a/runtime/common/construct_runtime/mod.rs
+++ b/runtime/common/construct_runtime/mod.rs
@@ -85,7 +85,7 @@
Ethereum: pallet_ethereum::{Pallet, Config, Call, Storage, Event, Origin} = 101,
EvmCoderSubstrate: pallet_evm_coder_substrate::{Pallet, Storage} = 150,
- EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage} = 151,
+ EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage, Event<T>} = 151,
EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152,
EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153,
}
tests/src/eth/api/ContractHelpers.soldiffbeforeafterboth--- a/tests/src/eth/api/ContractHelpers.sol
+++ b/tests/src/eth/api/ContractHelpers.sol
@@ -12,9 +12,19 @@
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
+/// @dev inlined interface
+interface ContractHelpersEvents {
+ event ContractSponsorSet(address indexed contractAddress, address sponsor);
+ event ContractSponsorshipConfirmed(
+ address indexed contractAddress,
+ address sponsor
+ );
+ event ContractSponsorRemoved(address indexed contractAddress);
+}
+
/// @title Magic contract, which allows users to reconfigure other contracts
/// @dev the ERC-165 identifier for this interface is 0xd77fab70
-interface ContractHelpers is Dummy, ERC165 {
+interface ContractHelpers is Dummy, ERC165, ContractHelpersEvents {
/// Get user, which deployed specified contract
/// @dev May return zero address in case if contract is deployed
/// using uniquenetwork evm-migration pallet, or using other terms not
tests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth1// 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/>.1617import {expect} from 'chai';18import {19 contractHelpers,20 createEthAccountWithBalance,21 transferBalanceToEth,22 deployFlipper,23 itWeb3,24 SponsoringMode,25 createEthAccount,26 ethBalanceViaSub,27} from './util/helpers';2829describe('Sponsoring EVM contracts', () => {30 itWeb3('Self sponsored can be set by the address that deployed the contract', async ({api, web3, privateKeyWrapper}) => {31 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);32 const flipper = await deployFlipper(web3, owner);33 const helpers = contractHelpers(web3, owner);34 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;35 await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected;36 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;37 });3839 itWeb3('Self sponsored can not be set by the address that did not deployed the contract', async ({api, web3, privateKeyWrapper}) => {40 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);41 const notOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);42 const flipper = await deployFlipper(web3, owner);43 const helpers = contractHelpers(web3, owner);44 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;45 await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');46 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;47 });4849 itWeb3('Sponsoring can be set by the address that has deployed the contract', async ({api, web3, privateKeyWrapper}) => {50 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);51 const flipper = await deployFlipper(web3, owner);52 const helpers = contractHelpers(web3, owner);53 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;54 await expect(helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner})).to.be.not.rejected;55 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;56 });5758 itWeb3('Sponsoring cannot be set by the address that did not deployed the contract', async ({api, web3, privateKeyWrapper}) => {59 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);60 const notOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);61 const flipper = await deployFlipper(web3, owner);62 const helpers = contractHelpers(web3, owner);63 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;64 await expect(helpers.methods.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).call({from: notOwner})).to.be.rejectedWith('NoPermission');65 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;66 });67 68 itWeb3('Sponsor can be set by the address that deployed the contract', async ({api, web3, privateKeyWrapper}) => {69 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);70 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);71 const flipper = await deployFlipper(web3, owner);72 const helpers = contractHelpers(web3, owner);73 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;74 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;75 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true;76 });77 78 itWeb3('Sponsor can not be set by the address that did not deployed the contract', async ({api, web3, privateKeyWrapper}) => {79 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);80 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);81 const notOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);82 const flipper = await deployFlipper(web3, owner);83 const helpers = contractHelpers(web3, owner);84 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;85 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).call({from: notOwner})).to.be.rejectedWith('NoPermission');86 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;87 });8889 itWeb3('Sponsorship can be confirmed by the address that pending as sponsor', async ({api, web3, privateKeyWrapper}) => {90 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);91 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);92 const flipper = await deployFlipper(web3, owner);93 const helpers = contractHelpers(web3, owner);94 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;95 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;96 await expect(helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor})).to.be.not.rejected;97 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;98 });99100 itWeb3('Sponsorship can not be confirmed by the address that not pending as sponsor', async ({api, web3, privateKeyWrapper}) => {101 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);102 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);103 const notSponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);104 const flipper = await deployFlipper(web3, owner);105 const helpers = contractHelpers(web3, owner);106 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;107 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;108 await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPermission');109 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;110 });111112 itWeb3('Sponsorship can not be confirmed by the address that not set as sponsor', async ({api, web3, privateKeyWrapper}) => {113 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);114 const notSponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);115 const flipper = await deployFlipper(web3, owner);116 const helpers = contractHelpers(web3, owner);117 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;118 await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPendingSponsor');119 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;120 });121122 itWeb3('Get self sponsored sponsor', async ({api, web3, privateKeyWrapper}) => {123 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);124 const flipper = await deployFlipper(web3, owner);125 const helpers = contractHelpers(web3, owner);126 await helpers.methods.selfSponsoredEnable(flipper.options.address).send();127 128 const result = await helpers.methods.getSponsor(flipper.options.address).call();129130 expect(result[0]).to.be.eq(flipper.options.address);131 expect(result[1]).to.be.eq('0');132 });133134 itWeb3('Get confirmed sponsor', async ({api, web3, privateKeyWrapper}) => {135 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);136 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);137 const flipper = await deployFlipper(web3, owner);138 const helpers = contractHelpers(web3, owner);139 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();140 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});141 142 const result = await helpers.methods.getSponsor(flipper.options.address).call();143144 expect(result[0]).to.be.eq(sponsor);145 expect(result[1]).to.be.eq('0');146 });147148 itWeb3('Sponsor can be removed by the address that deployed the contract', async ({api, web3, privateKeyWrapper}) => {149 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);150 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);151 const flipper = await deployFlipper(web3, owner);152 const helpers = contractHelpers(web3, owner);153154 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;155 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();156 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});157 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;158 159 await helpers.methods.removeSponsor(flipper.options.address).send();160 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;161 });162163 itWeb3('Sponsor can not be removed by the address that did not deployed the contract', async ({api, web3, privateKeyWrapper}) => {164 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);165 const notOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);166 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);167 const flipper = await deployFlipper(web3, owner);168 const helpers = contractHelpers(web3, owner);169170 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;171 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();172 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});173 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;174 175 await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');176 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;177 });178179 itWeb3('In generous mode, non-allowlisted user transaction will be sponsored', async ({api, web3, privateKeyWrapper}) => {180 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);181 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);182 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);183184 const flipper = await deployFlipper(web3, owner);185186 const helpers = contractHelpers(web3, owner);187188 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();189 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});190191 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});192 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});193194 const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);195 const callerBalanceBefore = await ethBalanceViaSub(api, caller);196197 await flipper.methods.flip().send({from: caller});198 expect(await flipper.methods.getValue().call()).to.be.true;199200 // Balance should be taken from sponsor instead of caller201 const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);202 const callerBalanceAfter = await ethBalanceViaSub(api, caller);203 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;204 expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);205 });206207 itWeb3('In generous mode, non-allowlisted user transaction will be self sponsored', async ({api, web3, privateKeyWrapper}) => {208 const alice = privateKeyWrapper('//Alice');209210 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);211 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);212213 const flipper = await deployFlipper(web3, owner);214215 const helpers = contractHelpers(web3, owner);216217 await helpers.methods.selfSponsoredEnable(flipper.options.address).send();218219 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});220 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});221222 await transferBalanceToEth(api, alice, flipper.options.address);223224 const contractBalanceBefore = await ethBalanceViaSub(api, flipper.options.address);225 const callerBalanceBefore = await ethBalanceViaSub(api, caller);226227 await flipper.methods.flip().send({from: caller});228 expect(await flipper.methods.getValue().call()).to.be.true;229230 // Balance should be taken from sponsor instead of caller231 const contractBalanceAfter = await ethBalanceViaSub(api, flipper.options.address);232 const callerBalanceAfter = await ethBalanceViaSub(api, caller);233 expect(contractBalanceAfter < contractBalanceBefore).to.be.true;234 expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);235 });236237 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}) => {238 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);239 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);240 const caller = createEthAccount(web3);241242 const flipper = await deployFlipper(web3, owner);243244 const helpers = contractHelpers(web3, owner);245 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});246 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});247248 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});249 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});250251 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();252 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});253254 const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);255 expect(sponsorBalanceBefore).to.be.not.equal('0');256257 await flipper.methods.flip().send({from: caller});258 expect(await flipper.methods.getValue().call()).to.be.true;259260 // Balance should be taken from flipper instead of caller261 const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);262 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;263 });264265 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}) => {266 const alice = privateKeyWrapper('//Alice');267268 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);269 const caller = createEthAccount(web3);270271 const flipper = await deployFlipper(web3, owner);272273 const helpers = contractHelpers(web3, owner);274275 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});276 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});277278 await transferBalanceToEth(api, alice, flipper.options.address);279280 const originalFlipperBalance = await web3.eth.getBalance(flipper.options.address);281 expect(originalFlipperBalance).to.be.not.equal('0');282283 await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/InvalidTransaction::Payment/);284 expect(await flipper.methods.getValue().call()).to.be.false;285286 // Balance should be taken from flipper instead of caller287 const balanceAfter = await web3.eth.getBalance(flipper.options.address);288 expect(+balanceAfter).to.be.equals(+originalFlipperBalance);289 });290291 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}) => {292 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);293 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);294 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);295296 const flipper = await deployFlipper(web3, owner);297298 const helpers = contractHelpers(web3, owner);299 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});300 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});301302 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});303 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});304305 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();306 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});307308 const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);309 const callerBalanceBefore = await ethBalanceViaSub(api, caller);310311 await flipper.methods.flip().send({from: caller});312 expect(await flipper.methods.getValue().call()).to.be.true;313314 const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);315 const callerBalanceAfter = await ethBalanceViaSub(api, caller);316 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;317 expect(callerBalanceAfter).to.be.equals(callerBalanceBefore);318 });319320 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}) => {321 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);322 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);323 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);324 const originalCallerBalance = await web3.eth.getBalance(caller);325326 const flipper = await deployFlipper(web3, owner);327328 const helpers = contractHelpers(web3, owner);329 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});330 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});331332 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});333 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 10).send({from: owner});334335 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();336 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});337338 const originalFlipperBalance = await web3.eth.getBalance(sponsor);339 expect(originalFlipperBalance).to.be.not.equal('0');340341 await flipper.methods.flip().send({from: caller});342 expect(await flipper.methods.getValue().call()).to.be.true;343 expect(await web3.eth.getBalance(caller)).to.be.equals(originalCallerBalance);344345 const newFlipperBalance = await web3.eth.getBalance(sponsor);346 expect(newFlipperBalance).to.be.not.equals(originalFlipperBalance);347348 await flipper.methods.flip().send({from: caller});349 expect(await web3.eth.getBalance(sponsor)).to.be.equal(newFlipperBalance);350 expect(await web3.eth.getBalance(caller)).to.be.not.equals(originalCallerBalance);351 });352353 // TODO: Find a way to calculate default rate limit354 itWeb3('Default rate limit equals 7200', async ({api, web3, privateKeyWrapper}) => {355 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);356 const flipper = await deployFlipper(web3, owner);357 const helpers = contractHelpers(web3, owner);358 expect(await helpers.methods.getSponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200');359 });360});tests/src/eth/util/contractHelpersAbi.jsondiffbeforeafterboth--- a/tests/src/eth/util/contractHelpersAbi.json
+++ b/tests/src/eth/util/contractHelpersAbi.json
@@ -1,5 +1,56 @@
[
{
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "contractAddress",
+ "type": "address"
+ }
+ ],
+ "name": "ContractSponsorRemoved",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "contractAddress",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "sponsor",
+ "type": "address"
+ }
+ ],
+ "name": "ContractSponsorSet",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "contractAddress",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "sponsor",
+ "type": "address"
+ }
+ ],
+ "name": "ContractSponsorshipConfirmed",
+ "type": "event"
+ },
+ {
"inputs": [
{
"internalType": "address",