--- a/Cargo.lock +++ b/Cargo.lock @@ -113,6 +113,20 @@ checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8" [[package]] +name = "app-promotion-rpc" +version = "0.1.0" +dependencies = [ + "pallet-common", + "pallet-evm", + "parity-scale-codec 3.1.5", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", + "up-data-structs", +] + +[[package]] name = "approx" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5127,6 +5141,7 @@ name = "opal-runtime" version = "0.9.27" dependencies = [ + "app-promotion-rpc", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -5150,6 +5165,7 @@ "hex-literal", "log", "orml-vesting", + "pallet-app-promotion", "pallet-aura", "pallet-balances", "pallet-base-fee", @@ -5296,6 +5312,31 @@ ] [[package]] +name = "pallet-app-promotion" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-common", + "pallet-evm", + "pallet-evm-contract-helpers", + "pallet-evm-migration", + "pallet-randomness-collective-flip", + "pallet-timestamp", + "pallet-unique", + "parity-scale-codec 3.1.5", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "up-data-structs", +] + +[[package]] name = "pallet-aura" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.27#8eff668a42325aeb4433eace1604f4d286a6ec05" @@ -5694,6 +5735,7 @@ name = "pallet-evm-contract-helpers" version = "0.2.0" dependencies = [ + "ethereum", "evm-coder", "fp-evm-mapping", "frame-support", @@ -6432,7 +6474,7 @@ [[package]] name = "pallet-unique" -version = "0.1.3" +version = "0.1.4" dependencies = [ "ethereum", "evm-coder", @@ -8332,6 +8374,7 @@ name = "quartz-runtime" version = "0.9.27" dependencies = [ + "app-promotion-rpc", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -8355,6 +8398,7 @@ "hex-literal", "log", "orml-vesting", + "pallet-app-promotion", "pallet-aura", "pallet-balances", "pallet-base-fee", @@ -12103,9 +12147,10 @@ [[package]] name = "uc-rpc" -version = "0.1.3" +version = "0.1.4" dependencies = [ "anyhow", + "app-promotion-rpc", "jsonrpsee", "pallet-common", "pallet-evm", @@ -12184,6 +12229,7 @@ name = "unique-node" version = "0.9.27" dependencies = [ + "app-promotion-rpc", "clap", "cumulus-client-cli", "cumulus-client-collator", @@ -12272,6 +12318,7 @@ name = "unique-rpc" version = "0.1.1" dependencies = [ + "app-promotion-rpc", "fc-db", "fc-mapping-sync", "fc-rpc", @@ -12321,6 +12368,7 @@ name = "unique-runtime" version = "0.9.27" dependencies = [ + "app-promotion-rpc", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -12344,6 +12392,7 @@ "hex-literal", "log", "orml-vesting", + "pallet-app-promotion", "pallet-aura", "pallet-balances", "pallet-base-fee", --- a/Makefile +++ b/Makefile @@ -128,5 +128,9 @@ bench-rmrk-equip: make _bench PALLET=proxy-rmrk-equip +.PHONY: bench-app-promotion +bench-app-promotion: + make _bench PALLET=app-promotion PALLET_DIR=app-promotion + .PHONY: bench bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-scheduler bench-rmrk-core bench-rmrk-equip --- a/client/rpc/CHANGELOG.md +++ b/client/rpc/CHANGELOG.md @@ -3,15 +3,21 @@ All notable changes to this project will be documented in this file. + +## [v0.1.4] 2022-09-08 + +### Added +- Support RPC for `AppPromotion` pallet. + ## [v0.1.3] 2022-08-16 ### Other changes -- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a +- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a -- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 +- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 -- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b +- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b ## [0.1.2] - 2022-08-12 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uc-rpc" -version = "0.1.3" +version = "0.1.4" license = "GPLv3" edition = "2021" @@ -8,6 +8,7 @@ pallet-common = { default-features = false, path = '../../pallets/common' } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } up-rpc = { path = "../../primitives/rpc" } +app-promotion-rpc = { path = "../../primitives/app_promotion_rpc"} rmrk-rpc = { path = "../../primitives/rmrk-rpc" } codec = { package = "parity-scale-codec", version = "3.1.2" } jsonrpsee = { version = "0.14.0", features = ["server", "macros"] } --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -23,6 +23,7 @@ proc_macros::rpc, }; use anyhow::anyhow; +use sp_runtime::traits::{AtLeast32BitUnsigned, Member}; use up_data_structs::{ RpcCollection, CollectionId, CollectionStats, CollectionLimits, TokenId, Property, PropertyKeyPermission, TokenData, TokenChild, @@ -30,6 +31,7 @@ use sp_api::{BlockId, BlockT, ProvideRuntimeApi, ApiExt}; use sp_blockchain::HeaderBackend; use up_rpc::UniqueApi as UniqueRuntimeApi; +use app_promotion_rpc::AppPromotionApi as AppPromotionRuntimeApi; // RMRK use rmrk_rpc::RmrkApi as RmrkRuntimeApi; @@ -37,6 +39,7 @@ RmrkCollectionId, RmrkNftId, RmrkBaseId, RmrkNftChild, RmrkThemeName, RmrkResourceId, }; +pub use app_promotion_unique_rpc::AppPromotionApiServer; pub use rmrk_unique_rpc::RmrkApiServer; #[rpc(server)] @@ -245,6 +248,46 @@ ) -> Result>; } +mod app_promotion_unique_rpc { + use super::*; + + #[rpc(server)] + #[async_trait] + pub trait AppPromotionApi { + /// Returns the total amount of staked tokens. + #[method(name = "appPromotion_totalStaked")] + fn total_staked( + &self, + staker: Option, + at: Option, + ) -> Result; + + ///Returns the total amount of staked tokens per block when staked. + #[method(name = "appPromotion_totalStakedPerBlock")] + fn total_staked_per_block( + &self, + staker: CrossAccountId, + at: Option, + ) -> Result>; + + /// Returns the total amount of tokens pending withdrawal from staking. + #[method(name = "appPromotion_pendingUnstake")] + fn pending_unstake( + &self, + staker: Option, + at: Option, + ) -> Result; + + /// Returns the total amount of tokens pending withdrawal from staking per block. + #[method(name = "appPromotion_pendingUnstakePerBlock")] + fn pending_unstake_per_block( + &self, + staker: CrossAccountId, + at: Option, + ) -> Result>; + } +} + mod rmrk_unique_rpc { use super::*; @@ -367,34 +410,28 @@ } } -pub struct Unique { - client: Arc, - _marker: std::marker::PhantomData

, -} +macro_rules! define_struct_for_server_api { + ($name:ident) => { + pub struct $name { + client: Arc, + _marker: std::marker::PhantomData

, + } -impl Unique { - pub fn new(client: Arc) -> Self { - Self { - client, - _marker: Default::default(), + impl $name { + pub fn new(client: Arc) -> Self { + Self { + client, + _marker: Default::default(), + } + } } - } + }; } -pub struct Rmrk { - client: Arc, - _marker: std::marker::PhantomData

, -} +define_struct_for_server_api!(Unique); +define_struct_for_server_api!(AppPromotion); +define_struct_for_server_api!(Rmrk); -impl Rmrk { - pub fn new(client: Arc) -> Self { - Self { - client, - _marker: Default::default(), - } - } -} - macro_rules! pass_method { ( $method_name:ident( @@ -440,6 +477,12 @@ }; } +macro_rules! app_promotion_api { + () => { + dyn AppPromotionRuntimeApi + }; +} + macro_rules! rmrk_api { () => { dyn RmrkRuntimeApi @@ -522,6 +565,35 @@ pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec, unique_api); } +impl + app_promotion_unique_rpc::AppPromotionApiServer< + ::Hash, + BlockNumber, + CrossAccountId, + AccountId, + > for AppPromotion +where + Block: BlockT, + BlockNumber: Decode + Member + AtLeast32BitUnsigned, + AccountId: Decode, + C: 'static + ProvideRuntimeApi + HeaderBackend, + C::Api: AppPromotionRuntimeApi, + CrossAccountId: pallet_evm::account::CrossAccountId, +{ + pass_method!(total_staked(staker: Option) -> String => |v| v.to_string(), app_promotion_api); + pass_method!(total_staked_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> => + |v| v + .into_iter() + .map(|(b, a)| (b, a.to_string())) + .collect::>(), app_promotion_api); + pass_method!(pending_unstake(staker: Option) -> String => |v| v.to_string(), app_promotion_api); + pass_method!(pending_unstake_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> => + |v| v + .into_iter() + .map(|(b, a)| (b, a.to_string())) + .collect::>(), app_promotion_api); +} + #[allow(deprecated)] impl< C, --- /dev/null +++ b/doc/separate_rpc.md @@ -0,0 +1,123 @@ +1. Create, in the `primitives` folder, a crate with a trait for RPC generation. + ```rust + sp_api::decl_runtime_apis! { + #[api_version(2)] + pub trait ModuleNameApi + where + CrossAccountId: pallet_evm::account::CrossAccountId, + { + fn method_name(user: Option) -> Result; + } + } + ``` + +2. client/rpc/src/lib.rs + * Add a trait with the required methods. Mark it with `#[rpc(server)]` and `#[async_trait]` directives. + ```rust + #[rpc(server)] + #[async_trait] + pub trait ModuleNameApi { + #[method(name = "moduleName_methodName")] + fn method_name(&self, user: Option, at: Option) + -> Result; + } + ``` + * Don't forget to write the correct method identifier in the form `moduleName_methodName`. + * Add a structure for which the server API interface will be implemented. + ```rust + define_struct_for_server_api!(ModuleName); + ``` + * Define a macro to be used in the implementation of the server API interface. + ```rust + macro_rules! module_api { + () => { + dyn ModuleNameRuntimeApi + }; + } + ``` + * Implement a server API interface. + ```rust + impl + ModuleNameApiServer<::Hash, CrossAccountId> for ModuleName + where + Block: BlockT, + C: 'static + ProvideRuntimeApi + HeaderBackend, + C::Api: AppPromotionRuntimeApi, + CrossAccountId: pallet_evm::account::CrossAccountId, + { + pass_method!(method_name(user: Option) -> String => |v| v.to_string(), app_promotion_api); + } + ``` + +3. runtime/common/runtime_apis.rs + * Implement the `ModuleNameApi` interface for `Runtime`. Optionally, you can mark a feature flag to disable the functionality. + ```rust + impl MethodApi for Runtime { + fn method_name(user: Option) -> Result { + #[cfg(not(feature = "module"))] + return unsupported!(); + + #[cfg(feature = "module")] + return Ok(0); + } + } + ``` + +4. node/cli/src/service.rs + * Set the `MethodApi` bound in the `start_node_impl`, `start_node`, `start_dev_node` methods. + +5. node/rpc/src/lib.rs + * Add `MethodApi` bound to `create_full` method. + * Enable RPC in the `create_full` method by adding `io.merge(ModuleName::new(client.clone()).into_rpc())?;` + +6. Add a new crate (see point 1) into dependencies. + * client/rpc/Cargo.toml + * node/rpc/Cargo.toml + * runtime/opal/Cargo.toml + * runtime/quartz/Cargo.toml + * runtime/unique/Cargo.toml + +7. Create tests/src/interfaces/ModuleName/definitions.ts and describe the necessary methods in it. + ```ts + type RpcParam = { + name: string; + type: string; + isOptional?: true; + }; + + const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr'; + + const fun = (description: string, params: RpcParam[], type: string) => ({ + description, + params: [...params, atParam], + type, + }); + + export default { + types: {}, + rpc: { + methodName: fun( + 'Documentation for method', + [{name: 'user', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}], + 'u128', + ), + }, + }; + ``` + +8. Describe definitions from paragraph 7 in tests/src/interfaces/definitions.ts. + ```ts + export {default as ModuleName} from './module/definitions'; + ``` + +9. tests/src/substrate/substrate-api.ts + * Set the RPC interface in the `defaultApiOptions` function, add an entry in the `rpc` parameter + ```ts + module: defs.module.rpc, + ``` + +10. tests/src/util/playgrounds/unique.dev.ts + * Specify RPC interface in `connect` function, add entry in `rpc` parameter + ```ts + module: defs.module.rpc, + ``` \ No newline at end of file --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -318,6 +318,7 @@ pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } unique-rpc = { default-features = false, path = "../rpc" } +app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false} rmrk-rpc = { path = "../../primitives/rmrk-rpc" } [features] --- a/node/cli/src/service.rs +++ b/node/cli/src/service.rs @@ -63,7 +63,9 @@ use fc_rpc_core::types::FilterPool; use fc_mapping_sync::{MappingSyncWorker, SyncStrategy}; -use up_common::types::opaque::{AuraId, RuntimeInstance, AccountId, Balance, Index, Hash, Block}; +use up_common::types::opaque::{ + AuraId, RuntimeInstance, AccountId, Balance, Index, Hash, Block, BlockNumber, +}; // RMRK use up_data_structs::{ @@ -362,6 +364,7 @@ + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi + sp_api::ApiExt> + up_rpc::UniqueApi + + app_promotion_rpc::AppPromotionApi + rmrk_rpc::RmrkApi< Block, AccountId, @@ -663,6 +666,7 @@ + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi + sp_api::ApiExt> + up_rpc::UniqueApi + + app_promotion_rpc::AppPromotionApi + rmrk_rpc::RmrkApi< Block, AccountId, @@ -807,6 +811,7 @@ + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi + sp_api::ApiExt> + up_rpc::UniqueApi + + app_promotion_rpc::AppPromotionApi + rmrk_rpc::RmrkApi< Block, AccountId, --- a/node/rpc/Cargo.toml +++ b/node/rpc/Cargo.toml @@ -53,6 +53,7 @@ pallet-unique = { path = "../../pallets/unique" } uc-rpc = { path = "../../client/rpc" } up-rpc = { path = "../../primitives/rpc" } +app-promotion-rpc = { path = "../../primitives/app_promotion_rpc"} rmrk-rpc = { path = "../../primitives/rmrk-rpc" } up-data-structs = { default-features = false, path = "../../primitives/data-structs" } --- a/node/rpc/src/lib.rs +++ b/node/rpc/src/lib.rs @@ -145,6 +145,12 @@ C::Api: fp_rpc::EthereumRuntimeRPCApi, C::Api: fp_rpc::ConvertTransactionRuntimeApi, C::Api: up_rpc::UniqueApi::CrossAccountId, AccountId>, + C::Api: app_promotion_rpc::AppPromotionApi< + Block, + BlockNumber, + ::CrossAccountId, + AccountId, + >, C::Api: rmrk_rpc::RmrkApi< Block, AccountId, @@ -169,6 +175,7 @@ EthPubSubApiServer, EthSigner, Net, NetApiServer, Web3, Web3ApiServer, }; use uc_rpc::{UniqueApiServer, Unique}; + use uc_rpc::{AppPromotionApiServer, AppPromotion}; #[cfg(not(feature = "unique-runtime"))] use uc_rpc::{RmrkApiServer, Rmrk}; @@ -227,6 +234,9 @@ io.merge(Unique::new(client.clone()).into_rpc())?; + #[cfg(not(any(feature = "unique-runtime", feature = "quartz-runtime")))] + io.merge(AppPromotion::new(client.clone()).into_rpc())?; + #[cfg(not(feature = "unique-runtime"))] io.merge(Rmrk::new(client.clone()).into_rpc())?; --- /dev/null +++ b/pallets/app-promotion/Cargo.toml @@ -0,0 +1,73 @@ +################################################################################ +# Package + +[package] +authors = ['Unique Network '] +description = 'Unique App Promotion Pallet' +edition = '2021' +homepage = 'https://unique.network' +license = 'GPLv3' +name = 'pallet-app-promotion' +repository = 'https://github.com/UniqueNetwork/unique-chain' +version = '0.1.0' + +[package.metadata.docs.rs] +targets = ['x86_64-unknown-linux-gnu'] + +[features] +default = ['std'] +runtime-benchmarks = [ + 'frame-benchmarking', + 'frame-support/runtime-benchmarks', + 'frame-system/runtime-benchmarks', + # 'pallet-unique/runtime-benchmarks', +] +std = [ + 'codec/std', + 'frame-benchmarking/std', + 'frame-support/std', + 'frame-system/std', + 'pallet-balances/std', + 'pallet-timestamp/std', + 'pallet-randomness-collective-flip/std', + 'pallet-evm/std', + 'sp-io/std', + 'sp-std/std', + 'sp-runtime/std', + 'sp-core/std', + 'serde/std', + +] +[dependencies] +scale-info = { version = "2.0.1", default-features = false, features = [ + "derive", +] } +################################################################################ +# Substrate Dependencies + +codec = { default-features = false, features = ['derive'], package = 'parity-scale-codec', version = '3.1.2' } +frame-benchmarking = {default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +frame-system ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +pallet-balances ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +pallet-timestamp ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +pallet-randomness-collective-flip ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +pallet-evm ={ default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +sp-std ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +sp-io ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +serde = { default-features = false, features = ['derive'], version = '1.0.130' } + +################################################################################ +# local dependencies + +up-data-structs ={ default-features = false, path = "../../primitives/data-structs" } +pallet-common ={ default-features = false, path = "../common" } +pallet-unique ={ default-features = false, path = "../unique" } +pallet-evm-contract-helpers ={ default-features = false, path = "../evm-contract-helpers" } + +[dev-dependencies] +pallet-evm-migration ={ default-features = false, path = "../evm-migration" } + +################################################################################ --- /dev/null +++ b/pallets/app-promotion/src/benchmarking.rs @@ -0,0 +1,162 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use crate::Pallet as PromototionPallet; + +use sp_runtime::traits::Bounded; +use sp_std::vec; + +use frame_benchmarking::{benchmarks, account}; +use frame_support::traits::OnInitialize; +use frame_system::{Origin, RawOrigin}; +use pallet_unique::benchmarking::create_nft_collection; +use pallet_evm_migration::Pallet as EvmMigrationPallet; + +const SEED: u32 = 0; + +fn set_admin() -> Result +where + T: Config + pallet_unique::Config + pallet_evm_migration::Config, + T::BlockNumber: From + Into, + <::Currency as Currency>::Balance: Sum + From, +{ + let pallet_admin = account::("admin", 0, SEED); + + ::Currency::make_free_balance_be( + &pallet_admin, + Perbill::from_rational(1u32, 2) * BalanceOf::::max_value(), + ); + + PromototionPallet::::set_admin_address( + RawOrigin::Root.into(), + T::CrossAccountId::from_sub(pallet_admin.clone()), + )?; + + Ok(pallet_admin) +} + +benchmarks! { + where_clause{ + where T: Config + pallet_unique::Config + pallet_evm_migration::Config , + T::BlockNumber: From + Into, + <::Currency as Currency>::Balance: Sum + From + } + + on_initialize { + let b in 0..PENDING_LIMIT_PER_BLOCK; + set_admin::()?; + + (0..b).try_for_each(|index| { + let staker = account::("staker", index, SEED); + ::Currency::make_free_balance_be(&staker, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + PromototionPallet::::stake(RawOrigin::Signed(staker.clone()).into(), Into::>::into(100u128) * T::Nominal::get())?; + PromototionPallet::::unstake(RawOrigin::Signed(staker.clone()).into()).map_err(|e| e.error)?; + Result::<(), sp_runtime::DispatchError>::Ok(()) + })?; + let block_number = >::current_block_number() + T::PendingInterval::get(); + }: {PromototionPallet::::on_initialize(block_number)} + + set_admin_address { + let pallet_admin = account::("admin", 0, SEED); + let _ = ::Currency::make_free_balance_be(&pallet_admin, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + } : _(RawOrigin::Root, T::CrossAccountId::from_sub(pallet_admin)) + + payout_stakers{ + let b in 1..101; + + let pallet_admin = account::("admin", 1, SEED); + let share = Perbill::from_rational(1u32, 20); + PromototionPallet::::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?; + ::Currency::make_free_balance_be(&pallet_admin, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + + let staker: T::AccountId = account("caller", 0, SEED); + ::Currency::make_free_balance_be(&staker, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + let stakers: Vec = (0..b).map(|index| account("staker", index, SEED)).collect(); + stakers.iter().for_each(|staker| { + ::Currency::make_free_balance_be(&staker, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + }); + (0..10).try_for_each(|_| { + stakers.iter() + .map(|staker| { + PromototionPallet::::stake(RawOrigin::Signed(staker.clone()).into(), Into::>::into(100u128) * T::Nominal::get()) + }).collect::, _>>()?; + >::finalize(); + Result::<(), sp_runtime::DispatchError>::Ok(()) + })?; + } : _(RawOrigin::Signed(pallet_admin.clone()), Some(b as u8)) + + stake { + let caller = account::("caller", 0, SEED); + let share = Perbill::from_rational(1u32, 10); + let _ = ::Currency::make_free_balance_be(&caller, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + } : _(RawOrigin::Signed(caller.clone()), share * ::Currency::total_balance(&caller)) + + unstake { + let caller = account::("caller", 0, SEED); + let share = Perbill::from_rational(1u32, 20); + let _ = ::Currency::make_free_balance_be(&caller, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + (0..10).map(|_| { + >::finalize(); + PromototionPallet::::stake(RawOrigin::Signed(caller.clone()).into(), share * ::Currency::total_balance(&caller)) + }).collect::, _>>()?; + + } : _(RawOrigin::Signed(caller.clone())) + + sponsor_collection { + let pallet_admin = account::("admin", 0, SEED); + PromototionPallet::::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?; + let _ = ::Currency::make_free_balance_be(&pallet_admin, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + let caller: T::AccountId = account("caller", 0, SEED); + let _ = ::Currency::make_free_balance_be(&caller, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + let collection = create_nft_collection::(caller.clone())?; + } : _(RawOrigin::Signed(pallet_admin.clone()), collection) + + stop_sponsoring_collection { + let pallet_admin = account::("admin", 0, SEED); + PromototionPallet::::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?; + let _ = ::Currency::make_free_balance_be(&pallet_admin, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + let caller: T::AccountId = account("caller", 0, SEED); + let _ = ::Currency::make_free_balance_be(&caller, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + let collection = create_nft_collection::(caller.clone())?; + PromototionPallet::::sponsor_collection(RawOrigin::Signed(pallet_admin.clone()).into(), collection)?; + } : _(RawOrigin::Signed(pallet_admin.clone()), collection) + + sponsor_contract { + let pallet_admin = account::("admin", 0, SEED); + PromototionPallet::::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?; + + let _ = ::Currency::make_free_balance_be(&pallet_admin, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + let address = H160::from_low_u64_be(SEED as u64); + let data: Vec = (0..20 as u8).collect(); + >::begin(RawOrigin::Root.into(), address)?; + >::finish(RawOrigin::Root.into(), address, data)?; + } : _(RawOrigin::Signed(pallet_admin.clone()), address) + + stop_sponsoring_contract { + let pallet_admin = account::("admin", 0, SEED); + PromototionPallet::::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?; + + let _ = ::Currency::make_free_balance_be(&pallet_admin, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); + let address = H160::from_low_u64_be(SEED as u64); + let data: Vec = (0..20 as u8).collect(); + >::begin(RawOrigin::Root.into(), address)?; + >::finish(RawOrigin::Root.into(), address, data)?; + PromototionPallet::::sponsor_contract(RawOrigin::Signed(pallet_admin.clone()).into(), address)?; + } : _(RawOrigin::Signed(pallet_admin.clone()), address) +} --- /dev/null +++ b/pallets/app-promotion/src/lib.rs @@ -0,0 +1,758 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +//! # App promotion +//! +//! The app promotion pallet is designed to ... . +//! +//! ## Interface +//! +//! ### Dispatchable Functions +//! + +// #![recursion_limit = "1024"] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +pub mod types; +pub mod weights; + +use sp_std::{ + vec::{Vec}, + vec, + iter::Sum, + borrow::ToOwned, + cell::RefCell, +}; +use sp_core::H160; +use codec::EncodeLike; +use pallet_balances::BalanceLock; +pub use types::*; + +use up_data_structs::CollectionId; + +use frame_support::{ + dispatch::{DispatchResult}, + traits::{ + Currency, Get, LockableCurrency, WithdrawReasons, tokens::Balance, ExistenceRequirement, + }, + ensure, +}; + +use weights::WeightInfo; + +pub use pallet::*; +use pallet_evm::account::CrossAccountId; +use sp_runtime::{ + Perbill, + traits::{BlockNumberProvider, CheckedAdd, CheckedSub, AccountIdConversion, Zero}, + ArithmeticError, +}; + +pub const LOCK_IDENTIFIER: [u8; 8] = *b"appstake"; + +const PENDING_LIMIT_PER_BLOCK: u32 = 3; + +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{ + Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, PalletId, + traits::ReservableCurrency, + }; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_evm::account::Config { + /// Type to interact with the native token + type Currency: ExtendedLockableCurrency + + ReservableCurrency; + + /// Type for interacting with collections + type CollectionHandler: CollectionHandler< + AccountId = Self::AccountId, + CollectionId = CollectionId, + >; + + /// Type for interacting with conrtacts + type ContractHandler: ContractHandler; + + /// ID for treasury + type TreasuryAccountId: Get; + + /// The app's pallet id, used for deriving its sovereign account ID. + #[pallet::constant] + type PalletId: Get; + + /// In relay blocks. + #[pallet::constant] + type RecalculationInterval: Get; + + /// In parachain blocks. + #[pallet::constant] + type PendingInterval: Get; + + /// Rate of return for interval in blocks defined in `RecalculationInterval`. + #[pallet::constant] + type IntervalIncome: Get; + + /// Decimals for the `Currency`. + #[pallet::constant] + type Nominal: Get>; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + // The relay block number provider + type RelayBlockNumberProvider: BlockNumberProvider; + + /// Events compatible with [`frame_system::Config::Event`]. + type Event: IsType<::Event> + From>; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(fn deposit_event)] + pub enum Event { + /// Staking recalculation was performed + /// + /// # Arguments + /// * AccountId: ID of the staker. + /// * Balance : recalculation base + /// * Balance : total income + StakingRecalculation( + /// An recalculated staker + T::AccountId, + /// Base on which interest is calculated + BalanceOf, + /// Amount of accrued interest + BalanceOf, + ), + + /// Staking was performed + /// + /// # Arguments + /// * AccountId: ID of the staker + /// * Balance : staking amount + Stake(T::AccountId, BalanceOf), + + /// Unstaking was performed + /// + /// # Arguments + /// * AccountId: ID of the staker + /// * Balance : unstaking amount + Unstake(T::AccountId, BalanceOf), + + /// The admin was set + /// + /// # Arguments + /// * AccountId: ID of the admin + SetAdmin(T::AccountId), + } + + #[pallet::error] + pub enum Error { + /// Error due to action requiring admin to be set. + AdminNotSet, + /// No permission to perform an action. + NoPermission, + /// Insufficient funds to perform an action. + NotSufficientFunds, + /// Occurs when a pending unstake cannot be added in this block. PENDING_LIMIT_PER_BLOCK` limits exceeded. + PendingForBlockOverflow, + /// The error is due to the fact that the collection/contract must already be sponsored in order to perform the action. + SponsorNotSet, + /// Errors caused by incorrect actions with a locked balance. + IncorrectLockedBalanceOperation, + } + + #[pallet::storage] + pub type TotalStaked = StorageValue, QueryKind = ValueQuery>; + + #[pallet::storage] + pub type Admin = StorageValue; + + /// Amount of tokens staked by account in the blocknumber. + #[pallet::storage] + pub type Staked = StorageNMap< + Key = ( + Key, + Key, + ), + Value = (BalanceOf, T::BlockNumber), + QueryKind = ValueQuery, + >; + /// Amount of stakes for an Account + #[pallet::storage] + pub type StakesPerAccount = + StorageMap<_, Blake2_128Concat, T::AccountId, u8, ValueQuery>; + + #[pallet::storage] + pub type PendingUnstake = StorageMap< + _, + Twox64Concat, + T::BlockNumber, + BoundedVec<(T::AccountId, BalanceOf), ConstU32>, + ValueQuery, + >; + + /// Stores a key for record for which the next revenue recalculation would be performed. + /// If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers. + #[pallet::storage] + #[pallet::getter(fn get_next_calculated_record)] + pub type NextCalculatedRecord = + StorageValue; + + #[pallet::hooks] + impl Hooks> for Pallet { + /// Block overflow is impossible due to the fact that the unstake algorithm in on_initialize + /// implies the execution of a strictly limited number of relatively lightweight operations. + /// A separate benchmark has been implemented to scale the weight depending on the number of pendings. + fn on_initialize(current_block_number: T::BlockNumber) -> Weight + where + ::BlockNumber: From, + { + let block_pending = PendingUnstake::::take(current_block_number); + let counter = block_pending.len() as u32; + + if !block_pending.is_empty() { + block_pending.into_iter().for_each(|(staker, amount)| { + >::unreserve(&staker, amount); + }); + } + + T::WeightInfo::on_initialize(counter) + } + } + + #[pallet::call] + impl Pallet + where + T::BlockNumber: From + Into, + <::Currency as Currency>::Balance: Sum + From, + { + #[pallet::weight(T::WeightInfo::set_admin_address())] + pub fn set_admin_address(origin: OriginFor, admin: T::CrossAccountId) -> DispatchResult { + ensure_root(origin)?; + + >::set(Some(admin.as_sub().to_owned())); + + Self::deposit_event(Event::SetAdmin(admin.as_sub().to_owned())); + + Ok(()) + } + + #[pallet::weight(T::WeightInfo::stake())] + pub fn stake(staker: OriginFor, amount: BalanceOf) -> DispatchResult { + let staker_id = ensure_signed(staker)?; + + ensure!( + StakesPerAccount::::get(&staker_id) < 10, + Error::::NoPermission + ); + + ensure!( + amount >= >::from(100u128) * T::Nominal::get(), + ArithmeticError::Underflow + ); + + let balance = + <::Currency as Currency>::free_balance(&staker_id); + + <::Currency as Currency>::ensure_can_withdraw( + &staker_id, + amount, + WithdrawReasons::all(), + balance + .checked_sub(&amount) + .ok_or(ArithmeticError::Underflow)?, + )?; + + Self::add_lock_balance(&staker_id, amount)?; + + let block_number = T::RelayBlockNumberProvider::current_block_number(); + + let recalculate_after_interval: T::BlockNumber = + if block_number % T::RecalculationInterval::get() == 0u32.into() { + 1u32.into() + } else { + 2u32.into() + }; + + let recalc_block = (block_number / T::RecalculationInterval::get() + + recalculate_after_interval) + * T::RecalculationInterval::get(); + + >::insert((&staker_id, block_number), { + let mut balance_and_recalc_block = >::get((&staker_id, block_number)); + balance_and_recalc_block.0 = balance_and_recalc_block + .0 + .checked_add(&amount) + .ok_or(ArithmeticError::Overflow)?; + balance_and_recalc_block.1 = recalc_block; + balance_and_recalc_block + }); + + >::set( + >::get() + .checked_add(&amount) + .ok_or(ArithmeticError::Overflow)?, + ); + + StakesPerAccount::::mutate(&staker_id, |stakes| *stakes += 1); + + Self::deposit_event(Event::Stake(staker_id, amount)); + + Ok(()) + } + + #[pallet::weight(T::WeightInfo::unstake())] + pub fn unstake(staker: OriginFor) -> DispatchResultWithPostInfo { + let staker_id = ensure_signed(staker)?; + let block = >::block_number() + T::PendingInterval::get(); + let mut pendings = >::get(block); + + ensure!(!pendings.is_full(), Error::::PendingForBlockOverflow); + + let mut total_stakes = 0u64; + + let total_staked: BalanceOf = Staked::::drain_prefix((&staker_id,)) + .map(|(_, (amount, _))| { + total_stakes += 1; + amount + }) + .sum(); + + if total_staked.is_zero() { + return Ok(None.into()); // TO-DO + } + + pendings + .try_push((staker_id.clone(), total_staked)) + .map_err(|_| Error::::PendingForBlockOverflow)?; + + >::insert(block, pendings); + + Self::unlock_balance(&staker_id, total_staked)?; + + >::reserve(&staker_id, total_staked)?; + + TotalStaked::::set( + TotalStaked::::get() + .checked_sub(&total_staked) + .ok_or(ArithmeticError::Underflow)?, + ); + + StakesPerAccount::::remove(&staker_id); + + Self::deposit_event(Event::Unstake(staker_id, total_staked)); + + Ok(None.into()) + } + + #[pallet::weight(T::WeightInfo::sponsor_collection())] + pub fn sponsor_collection( + admin: OriginFor, + collection_id: CollectionId, + ) -> DispatchResult { + let admin_id = ensure_signed(admin)?; + ensure!( + admin_id == Admin::::get().ok_or(Error::::AdminNotSet)?, + Error::::NoPermission + ); + + T::CollectionHandler::set_sponsor(Self::account_id(), collection_id) + } + #[pallet::weight(T::WeightInfo::stop_sponsoring_collection())] + pub fn stop_sponsoring_collection( + admin: OriginFor, + collection_id: CollectionId, + ) -> DispatchResult { + let admin_id = ensure_signed(admin)?; + + ensure!( + admin_id == Admin::::get().ok_or(Error::::AdminNotSet)?, + Error::::NoPermission + ); + + ensure!( + T::CollectionHandler::sponsor(collection_id)?.ok_or(>::SponsorNotSet)? + == Self::account_id(), + >::NoPermission + ); + T::CollectionHandler::remove_collection_sponsor(collection_id) + } + + #[pallet::weight(T::WeightInfo::sponsor_contract())] + pub fn sponsor_contract(admin: OriginFor, contract_id: H160) -> DispatchResult { + let admin_id = ensure_signed(admin)?; + + ensure!( + admin_id == Admin::::get().ok_or(Error::::AdminNotSet)?, + Error::::NoPermission + ); + + T::ContractHandler::set_sponsor( + T::CrossAccountId::from_sub(Self::account_id()), + contract_id, + ) + } + + #[pallet::weight(T::WeightInfo::stop_sponsoring_contract())] + pub fn stop_sponsoring_contract(admin: OriginFor, contract_id: H160) -> DispatchResult { + let admin_id = ensure_signed(admin)?; + + ensure!( + admin_id == Admin::::get().ok_or(Error::::AdminNotSet)?, + Error::::NoPermission + ); + + ensure!( + T::ContractHandler::sponsor(contract_id)? + .ok_or(>::SponsorNotSet)? + .as_sub() == &Self::account_id(), + >::NoPermission + ); + T::ContractHandler::remove_contract_sponsor(contract_id) + } + + #[pallet::weight(T::WeightInfo::payout_stakers(stakers_number.unwrap_or(20) as u32))] + pub fn payout_stakers(admin: OriginFor, stakers_number: Option) -> DispatchResult { + let admin_id = ensure_signed(admin)?; + + ensure!( + admin_id == Admin::::get().ok_or(Error::::AdminNotSet)?, + Error::::NoPermission + ); + + let current_recalc_block = + Self::get_current_recalc_block(T::RelayBlockNumberProvider::current_block_number()); + let next_recalc_block = current_recalc_block + T::RecalculationInterval::get(); + + let mut storage_iterator = Self::get_next_calculated_key() + .map_or(Staked::::iter(), |key| Staked::::iter_from(key)); + + NextCalculatedRecord::::set(None); + + // { + // let mut stakers_number = stakers_number.unwrap_or(20); + // let mut last_id = admin_id; + // let mut income_acc = BalanceOf::::default(); + // let mut amount_acc = BalanceOf::::default(); + + // while let Some(( + // (current_id, staked_block), + // (amount, next_recalc_block_for_stake), + // )) = storage_iterator.next() + // { + // if last_id != current_id { + // if income_acc != BalanceOf::::default() { + // >::transfer( + // &T::TreasuryAccountId::get(), + // &last_id, + // income_acc, + // ExistenceRequirement::KeepAlive, + // ) + // .and_then(|_| Self::add_lock_balance(&last_id, income_acc))?; + + // Self::deposit_event(Event::StakingRecalculation( + // last_id, amount, income_acc, + // )); + // } + + // if stakers_number == 0 { + // NextCalculatedRecord::::set(Some((current_id, staked_block))); + // break; + // } + // stakers_number -= 1; + // income_acc = BalanceOf::::default(); + // last_id = current_id; + // }; + // if current_recalc_block >= next_recalc_block_for_stake { + // Self::recalculate_and_insert_stake( + // &last_id, + // staked_block, + // next_recalc_block, + // amount, + // ((current_recalc_block - next_recalc_block_for_stake) + // / T::RecalculationInterval::get()) + // .into() + 1, + // &mut income_acc, + // ); + // } + // } + // } + + { + let mut stakers_number = stakers_number.unwrap_or(20); + let last_id = RefCell::new(None); + let income_acc = RefCell::new(BalanceOf::::default()); + let amount_acc = RefCell::new(BalanceOf::::default()); + + let flush_stake = || -> DispatchResult { + if let Some(last_id) = &*last_id.borrow() { + if !income_acc.borrow().is_zero() { + >::transfer( + &T::TreasuryAccountId::get(), + last_id, + *income_acc.borrow(), + ExistenceRequirement::KeepAlive, + ) + .and_then(|_| { + Self::add_lock_balance(last_id, *income_acc.borrow())?; + >::try_mutate(|staked| { + staked + .checked_add(&*income_acc.borrow()) + .ok_or(ArithmeticError::Overflow.into()) + }) + })?; + + Self::deposit_event(Event::StakingRecalculation( + last_id.clone(), + *amount_acc.borrow(), + *income_acc.borrow(), + )); + } + + *income_acc.borrow_mut() = BalanceOf::::default(); + *amount_acc.borrow_mut() = BalanceOf::::default(); + } + Ok(()) + }; + + while let Some(( + (current_id, staked_block), + (amount, next_recalc_block_for_stake), + )) = storage_iterator.next() + { + if stakers_number == 0 { + NextCalculatedRecord::::set(Some((current_id, staked_block))); + break; + } + if last_id.borrow().as_ref() != Some(¤t_id) { + flush_stake()?; + *last_id.borrow_mut() = Some(current_id.clone()); + stakers_number -= 1; + }; + if current_recalc_block >= next_recalc_block_for_stake { + *amount_acc.borrow_mut() += amount; + Self::recalculate_and_insert_stake( + ¤t_id, + staked_block, + next_recalc_block, + amount, + ((current_recalc_block - next_recalc_block_for_stake) + / T::RecalculationInterval::get()) + .into() + 1, + &mut *income_acc.borrow_mut(), + ); + } + } + flush_stake()?; + } + + Ok(()) + } + } +} + +impl Pallet { + pub fn account_id() -> T::AccountId { + T::PalletId::get().into_account_truncating() + } + + fn unlock_balance(staker: &T::AccountId, amount: BalanceOf) -> DispatchResult { + let locked_balance = Self::get_locked_balance(staker) + .map(|l| l.amount) + .ok_or(>::IncorrectLockedBalanceOperation)?; + + // It is understood that we cannot unlock more funds than were locked by staking. + // Therefore, if implemented correctly, this error should not occur. + Self::set_lock_unchecked( + staker, + locked_balance + .checked_sub(&amount) + .ok_or(ArithmeticError::Underflow)?, + ); + Ok(()) + } + + fn add_lock_balance(staker: &T::AccountId, amount: BalanceOf) -> DispatchResult { + Self::get_locked_balance(staker) + .map_or(>::default(), |l| l.amount) + .checked_add(&amount) + .map(|new_lock| Self::set_lock_unchecked(staker, new_lock)) + .ok_or(ArithmeticError::Overflow.into()) + } + + fn set_lock_unchecked(staker: &T::AccountId, amount: BalanceOf) { + if amount.is_zero() { + >::remove_lock(LOCK_IDENTIFIER, &staker); + } else { + >::set_lock( + LOCK_IDENTIFIER, + staker, + amount, + WithdrawReasons::all(), + ) + } + } + + pub fn get_locked_balance( + staker: impl EncodeLike, + ) -> Option>> { + >::locks(staker) + .into_iter() + .find(|l| l.id == LOCK_IDENTIFIER) + } + + pub fn total_staked_by_id(staker: impl EncodeLike) -> Option> { + let staked = Staked::::iter_prefix((staker,)) + .into_iter() + .fold(>::default(), |acc, (_, (amount, _))| { + acc + amount + }); + if staked != >::default() { + Some(staked) + } else { + None + } + } + + pub fn total_staked_by_id_per_block( + staker: impl EncodeLike, + ) -> Option)>> { + let mut staked = Staked::::iter_prefix((staker,)) + .into_iter() + .map(|(block, (amount, _))| (block, amount)) + .collect::>(); + staked.sort_by_key(|(block, _)| *block); + if !staked.is_empty() { + Some(staked) + } else { + None + } + } + + pub fn cross_id_total_staked(staker: Option) -> Option> { + staker.map_or(Some(>::get()), |s| { + Self::total_staked_by_id(s.as_sub()) + }) + } + + // pub fn cross_id_locked_balance(staker: T::CrossAccountId) -> BalanceOf { + // Self::get_locked_balance(staker.as_sub()) + // .map(|l| l.amount) + // .unwrap_or_default() + // } + + pub fn cross_id_total_staked_per_block( + staker: T::CrossAccountId, + ) -> Vec<(T::BlockNumber, BalanceOf)> { + Self::total_staked_by_id_per_block(staker.as_sub()).unwrap_or_default() + } + + fn recalculate_and_insert_stake( + staker: &T::AccountId, + staked_block: T::BlockNumber, + next_recalc_block: T::BlockNumber, + base: BalanceOf, + iters: u32, + income_acc: &mut BalanceOf, + ) { + let income = Self::calculate_income(base, iters); + + base.checked_add(&income).map(|res| { + >::insert((staker, staked_block), (res, next_recalc_block)); + *income_acc += income; + }); + } + + fn calculate_income(base: I, iters: u32) -> I + where + I: EncodeLike> + Balance, + { + let mut income = base; + + (0..iters).for_each(|_| income += T::IntervalIncome::get() * income); + + income - base + } + + fn get_current_recalc_block(current_relay_block: T::BlockNumber) -> T::BlockNumber { + (current_relay_block / T::RecalculationInterval::get()) * T::RecalculationInterval::get() + } + + // fn get_next_recalc_block(current_relay_block: T::BlockNumber) -> T::BlockNumber { + // Self::get_current_recalc_block(current_relay_block) + T::RecalculationInterval::get() + // } + + fn get_next_calculated_key() -> Option> { + Self::get_next_calculated_record().map(|key| Staked::::hashed_key_for(key)) + } +} + +impl Pallet +where + <::Currency as Currency>::Balance: Sum, +{ + /// Since user funds are not transferred anywhere by staking, overflow protection is provided + /// at the level of the associated type `Balance` of `Currency` trait. In order to overflow, + /// the staker must have more funds on his account than the maximum set for `Balance` type. + pub fn cross_id_pending_unstake(staker: Option) -> BalanceOf { + staker.map_or( + PendingUnstake::::iter_values() + .flat_map(|pendings| pendings.into_iter().map(|(_, amount)| amount)) + .sum(), + |s| { + PendingUnstake::::iter_values() + .flatten() + .filter_map(|(id, amount)| { + if id == *s.as_sub() { + Some(amount) + } else { + None + } + }) + .sum() + }, + ) + } + + pub fn cross_id_pending_unstake_per_block( + staker: T::CrossAccountId, + ) -> Vec<(T::BlockNumber, BalanceOf)> { + let mut unsorted_res = vec![]; + PendingUnstake::::iter().for_each(|(block, pendings)| { + pendings.into_iter().for_each(|(id, amount)| { + if id == *staker.as_sub() { + unsorted_res.push((block, amount)); + }; + }) + }); + + unsorted_res.sort_by_key(|(block, _)| *block); + unsorted_res + } +} --- /dev/null +++ b/pallets/app-promotion/src/types.rs @@ -0,0 +1,107 @@ +use codec::EncodeLike; +use frame_support::{traits::LockableCurrency, WeakBoundedVec, Parameter, dispatch::DispatchResult}; + +use pallet_balances::{BalanceLock, Config as BalancesConfig, Pallet as PalletBalances}; +use pallet_common::CollectionHandle; + +use sp_runtime::DispatchError; +use up_data_structs::{CollectionId}; +use sp_std::borrow::ToOwned; +use pallet_evm_contract_helpers::{Pallet as EvmHelpersPallet, Config as EvmHelpersConfig}; + +pub trait ExtendedLockableCurrency: LockableCurrency { + fn locks(who: KArg) -> WeakBoundedVec, Self::MaxLocks> + where + KArg: EncodeLike; +} + +impl, I: 'static> ExtendedLockableCurrency + for PalletBalances +{ + fn locks(who: KArg) -> WeakBoundedVec, Self::MaxLocks> + where + KArg: EncodeLike, + { + Self::locks(who) + } +} + +pub trait CollectionHandler { + type CollectionId; + type AccountId; + + fn set_sponsor( + sponsor_id: Self::AccountId, + collection_id: Self::CollectionId, + ) -> DispatchResult; + + fn remove_collection_sponsor(collection_id: Self::CollectionId) -> DispatchResult; + + fn sponsor(collection_id: Self::CollectionId) + -> Result, DispatchError>; +} + +impl CollectionHandler for pallet_unique::Pallet { + type CollectionId = CollectionId; + + type AccountId = T::AccountId; + + fn set_sponsor( + sponsor_id: Self::AccountId, + collection_id: Self::CollectionId, + ) -> DispatchResult { + Self::force_set_sponsor(sponsor_id, collection_id) + } + + fn remove_collection_sponsor(collection_id: Self::CollectionId) -> DispatchResult { + Self::force_remove_collection_sponsor(collection_id) + } + + fn sponsor( + collection_id: Self::CollectionId, + ) -> Result, DispatchError> { + Ok(>::try_get(collection_id)? + .sponsorship + .sponsor() + .map(|acc| acc.to_owned())) + } +} + +pub trait ContractHandler { + type ContractId; + type AccountId; + + fn set_sponsor( + sponsor_id: Self::AccountId, + contract_address: Self::ContractId, + ) -> DispatchResult; + + fn remove_contract_sponsor(contract_address: Self::ContractId) -> DispatchResult; + + fn sponsor( + contract_address: Self::ContractId, + ) -> Result, DispatchError>; +} + +impl ContractHandler for EvmHelpersPallet { + type ContractId = sp_core::H160; + + type AccountId = T::CrossAccountId; + + fn set_sponsor( + sponsor_id: Self::AccountId, + contract_address: Self::ContractId, + ) -> DispatchResult { + Self::force_set_sponsor(contract_address, &sponsor_id) + } + + fn remove_contract_sponsor(contract_address: Self::ContractId) -> DispatchResult { + Self::force_remove_sponsor(contract_address) + } + + fn sponsor( + contract_address: Self::ContractId, + ) -> Result, DispatchError> { + Ok(Self::get_sponsor(contract_address)) + } +} --- /dev/null +++ b/pallets/app-promotion/src/weights.rs @@ -0,0 +1,209 @@ +// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs + +//! Autogenerated weights for pallet_app_promotion +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-09-07, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// target/release/unique-collator +// benchmark +// pallet +// --pallet +// pallet-app-promotion +// --wasm-execution +// compiled +// --extrinsic +// * +// --template +// .maintain/frame-weight-template.hbs +// --steps=50 +// --repeat=80 +// --heap-pages=4096 +// --output=./pallets/app-promotion/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_app_promotion. +pub trait WeightInfo { + fn on_initialize(b: u32, ) -> Weight; + fn set_admin_address() -> Weight; + fn payout_stakers(b: u32, ) -> Weight; + fn stake() -> Weight; + fn unstake() -> Weight; + fn sponsor_collection() -> Weight; + fn stop_sponsoring_collection() -> Weight; + fn sponsor_contract() -> Weight; + fn stop_sponsoring_contract() -> Weight; +} + +/// Weights for pallet_app_promotion using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: AppPromotion PendingUnstake (r:1 w:0) + // Storage: System Account (r:1 w:1) + fn on_initialize(b: u32, ) -> Weight { + (2_651_000 as Weight) + // Standard Error: 103_000 + .saturating_add((6_024_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight))) + } + // Storage: AppPromotion Admin (r:0 w:1) + fn set_admin_address() -> Weight { + (7_117_000 as Weight) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + // Storage: AppPromotion Admin (r:1 w:0) + // Storage: ParachainSystem ValidationData (r:1 w:0) + // Storage: AppPromotion NextCalculatedRecord (r:1 w:1) + // Storage: AppPromotion Staked (r:2 w:0) + fn payout_stakers(b: u32, ) -> Weight { + (9_958_000 as Weight) + // Standard Error: 8_000 + .saturating_add((4_406_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + // Storage: AppPromotion StakesPerAccount (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + // Storage: ParachainSystem ValidationData (r:1 w:0) + // Storage: AppPromotion Staked (r:1 w:1) + // Storage: AppPromotion TotalStaked (r:1 w:1) + fn stake() -> Weight { + (20_574_000 as Weight) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) + } + // Storage: AppPromotion PendingUnstake (r:1 w:1) + // Storage: AppPromotion Staked (r:2 w:1) + // Storage: Balances Locks (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: AppPromotion TotalStaked (r:1 w:1) + // Storage: AppPromotion StakesPerAccount (r:0 w:1) + fn unstake() -> Weight { + (31_703_000 as Weight) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) + } + // Storage: AppPromotion Admin (r:1 w:0) + // Storage: Common CollectionById (r:1 w:1) + fn sponsor_collection() -> Weight { + (12_932_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + // Storage: AppPromotion Admin (r:1 w:0) + // Storage: Common CollectionById (r:1 w:1) + fn stop_sponsoring_collection() -> Weight { + (12_453_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + // Storage: AppPromotion Admin (r:1 w:0) + // Storage: EvmContractHelpers Sponsoring (r:0 w:1) + fn sponsor_contract() -> Weight { + (11_952_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + // Storage: AppPromotion Admin (r:1 w:0) + // Storage: EvmContractHelpers Sponsoring (r:1 w:1) + fn stop_sponsoring_contract() -> Weight { + (12_538_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: AppPromotion PendingUnstake (r:1 w:0) + // Storage: System Account (r:1 w:1) + fn on_initialize(b: u32, ) -> Weight { + (2_651_000 as Weight) + // Standard Error: 103_000 + .saturating_add((6_024_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(b as Weight))) + } + // Storage: AppPromotion Admin (r:0 w:1) + fn set_admin_address() -> Weight { + (7_117_000 as Weight) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + // Storage: AppPromotion Admin (r:1 w:0) + // Storage: ParachainSystem ValidationData (r:1 w:0) + // Storage: AppPromotion NextCalculatedRecord (r:1 w:1) + // Storage: AppPromotion Staked (r:2 w:0) + fn payout_stakers(b: u32, ) -> Weight { + (9_958_000 as Weight) + // Standard Error: 8_000 + .saturating_add((4_406_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + // Storage: AppPromotion StakesPerAccount (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + // Storage: ParachainSystem ValidationData (r:1 w:0) + // Storage: AppPromotion Staked (r:1 w:1) + // Storage: AppPromotion TotalStaked (r:1 w:1) + fn stake() -> Weight { + (20_574_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) + .saturating_add(RocksDbWeight::get().writes(5 as Weight)) + } + // Storage: AppPromotion PendingUnstake (r:1 w:1) + // Storage: AppPromotion Staked (r:2 w:1) + // Storage: Balances Locks (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: AppPromotion TotalStaked (r:1 w:1) + // Storage: AppPromotion StakesPerAccount (r:0 w:1) + fn unstake() -> Weight { + (31_703_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) + .saturating_add(RocksDbWeight::get().writes(6 as Weight)) + } + // Storage: AppPromotion Admin (r:1 w:0) + // Storage: Common CollectionById (r:1 w:1) + fn sponsor_collection() -> Weight { + (12_932_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + // Storage: AppPromotion Admin (r:1 w:0) + // Storage: Common CollectionById (r:1 w:1) + fn stop_sponsoring_collection() -> Weight { + (12_453_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + // Storage: AppPromotion Admin (r:1 w:0) + // Storage: EvmContractHelpers Sponsoring (r:0 w:1) + fn sponsor_contract() -> Weight { + (11_952_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + // Storage: AppPromotion Admin (r:1 w:0) + // Storage: EvmContractHelpers Sponsoring (r:1 w:1) + fn stop_sponsoring_contract() -> Weight { + (12_538_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } +} --- a/pallets/common/Cargo.toml +++ b/pallets/common/Cargo.toml @@ -40,4 +40,7 @@ "up-data-structs/std", "pallet-evm/std", ] -runtime-benchmarks = ["frame-benchmarking"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "up-data-structs/runtime-benchmarks", +] --- 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" } --- 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(SubstrateRecorder); impl WithRecorder for ContractHelpers { @@ -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 ContractHelpers where T::AccountId: AsRef<[u8; 32]>, @@ -91,9 +122,17 @@ self.recorder().consume_sload()?; self.recorder().consume_sstore()?; - Pallet::::self_sponsored_enable(&T::CrossAccountId::from_eth(caller), contract_address) + let caller = T::CrossAccountId::from_eth(caller); + + Pallet::::ensure_owner(contract_address, *caller.as_eth()) .map_err(dispatch_to_evm::)?; + Pallet::::force_set_sponsor( + contract_address, + &T::CrossAccountId::from_eth(contract_address), + ) + .map_err(dispatch_to_evm::)?; + Ok(()) } --- 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<::Event> + From>; + /// Address, under which magic contract will be available type ContractAddress: Get; + /// In case of enabled sponsoring, but no sponsoring rate limit set, /// this value will be used implicitly type DefaultSponsoringRateLimit: Get; @@ -150,6 +156,32 @@ QueryKind = ValueQuery, >; + #[pallet::event] + #[pallet::generate_deposit(pub fn deposit_event)] + pub enum Event { + /// 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 Pallet { /// Get contract owner. pub fn contract_owner(contract: H160) -> H160 { @@ -169,43 +201,118 @@ contract, SponsorshipState::::Unconfirmed(sponsor.clone()), ); + + >::deposit_event(Event::::ContractSponsorSet( + contract, + sponsor.as_sub().clone(), + )); + >::deposit_log( + ContractHelpersEvents::ContractSponsorSet { + contract_address: contract, + sponsor: *sponsor.as_eth(), + } + .to_log(contract), + ); Ok(()) } - /// Set `contract` as self sponsored. + /// TO-DO /// - /// `sender` must be owner of contract. - pub fn self_sponsored_enable(sender: &T::CrossAccountId, contract: H160) -> DispatchResult { - Pallet::::ensure_owner(contract, *sender.as_eth())?; + /// + pub fn force_set_sponsor( + contract_address: H160, + sponsor: &T::CrossAccountId, + ) -> DispatchResult { Sponsoring::::insert( - contract, - SponsorshipState::::Confirmed(T::CrossAccountId::from_eth( - contract, - )), + contract_address, + SponsorshipState::::Confirmed(sponsor.clone()), ); + + let eth_sponsor = *sponsor.as_eth(); + let sub_sponsor = sponsor.as_sub().clone(); + + >::deposit_event(Event::::ContractSponsorSet( + contract_address, + sub_sponsor.clone(), + )); + >::deposit_log( + ContractHelpersEvents::ContractSponsorSet { + contract_address, + sponsor: eth_sponsor, + } + .to_log(contract_address), + ); + + >::deposit_event(Event::::ContractSponsorshipConfirmed( + contract_address, + sub_sponsor, + )); + >::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::::ensure_owner(contract, *sender.as_eth())?; - Sponsoring::::remove(contract); + pub fn remove_sponsor( + sender: &T::CrossAccountId, + contract_address: H160, + ) -> DispatchResult { + Self::ensure_owner(contract_address, *sender.as_eth())?; + Self::force_remove_sponsor(contract_address) + } + + /// TO-DO + /// + /// + pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult { + Sponsoring::::remove(contract_address); + + Self::deposit_event(Event::::ContractSponsorRemoved(contract_address)); + >::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::::get(contract) { + pub fn confirm_sponsorship( + sender: &T::CrossAccountId, + contract_address: H160, + ) -> DispatchResult { + match Sponsoring::::get(contract_address) { SponsorshipState::Unconfirmed(sponsor) => { ensure!(sponsor == *sender, Error::::NoPermission); + let eth_sponsor = *sponsor.as_eth(); + let sub_sponsor = sponsor.as_sub().clone(); Sponsoring::::insert( - contract, + contract_address, SponsorshipState::::Confirmed(sponsor), ); + + >::deposit_event(Event::::ContractSponsorshipConfirmed( + contract_address, + sub_sponsor, + )); + >::deposit_log( + ContractHelpersEvents::ContractSponsorshipConfirmed { + contract_address, + sponsor: eth_sponsor, + } + .to_log(contract_address), + ); + Ok(()) } SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => { --- 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 --- a/pallets/unique/CHANGELOG.md +++ b/pallets/unique/CHANGELOG.md @@ -3,22 +3,29 @@ All notable changes to this project will be documented in this file. + +## [v0.1.4] 2022-09-5 + +### Added + +- Methods `force_set_sponsor` , `force_remove_collection_sponsor` to be able to administer sponsorships with other pallets. Added to implement `AppPromotion` pallet logic. + ## [v0.1.3] 2022-08-16 ### Other changes -- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a +- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a -- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 +- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 -- refactor: Remove `#[transactional]` from extrinsics 7fd36cea2f6e00c02c67ccc1de9649ae404efd31 +- refactor: Remove `#[transactional]` from extrinsics 7fd36cea2f6e00c02c67ccc1de9649ae404efd31 Every extrinsic now runs in transaction implicitly, and `#[transactional]` on pallet dispatchable is now meaningless Upstream-Change: https://github.com/paritytech/substrate/issues/10806 -- refactor: Switch to new prefix removal methods 26734e9567589d75cdd99e404eabf11d5a97d975 +- refactor: Switch to new prefix removal methods 26734e9567589d75cdd99e404eabf11d5a97d975 New methods allows to call `remove_prefix` with limit multiple times in the same block @@ -27,10 +34,12 @@ Upstream-Change: https://github.com/paritytech/substrate/pull/11490 -- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b +- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b ## [v0.1.1] - 2022-07-25 + ### Added -- Method for creating `ERC721Metadata` compatible NFT collection. -- Method for creating `ERC721Metadata` compatible ReFungible collection. -- Method for creating ReFungible collection. + +- Method for creating `ERC721Metadata` compatible NFT collection. +- Method for creating `ERC721Metadata` compatible ReFungible collection. +- Method for creating ReFungible collection. --- a/pallets/unique/Cargo.toml +++ b/pallets/unique/Cargo.toml @@ -9,7 +9,7 @@ license = 'GPLv3' name = 'pallet-unique' repository = 'https://github.com/UniqueNetwork/unique-chain' -version = "0.1.3" +version = "0.1.4" [package.metadata.docs.rs] targets = ['x86_64-unknown-linux-gnu'] --- a/pallets/unique/src/benchmarking.rs +++ b/pallets/unique/src/benchmarking.rs @@ -46,7 +46,9 @@ )?; Ok(>::get()) } -fn create_nft_collection(owner: T::AccountId) -> Result { +pub fn create_nft_collection( + owner: T::AccountId, +) -> Result { create_collection_helper::(owner, CollectionMode::NFT) } --- a/pallets/unique/src/lib.rs +++ b/pallets/unique/src/lib.rs @@ -98,7 +98,7 @@ pub mod eth; #[cfg(feature = "runtime-benchmarks")] -mod benchmarking; +pub mod benchmarking; pub mod weights; use weights::WeightInfo; @@ -277,7 +277,7 @@ { type Error = Error; - fn deposit_event() = default; + pub fn deposit_event() = default; fn on_initialize(_now: T::BlockNumber) -> Weight { 0 @@ -1103,3 +1103,35 @@ } } } + +impl Pallet { + pub fn force_set_sponsor(sponsor: T::AccountId, collection_id: CollectionId) -> DispatchResult { + let mut target_collection = >::try_get(collection_id)?; + target_collection.check_is_internal()?; + target_collection.set_sponsor(sponsor.clone())?; + + Self::deposit_event(Event::::CollectionSponsorSet( + collection_id, + sponsor.clone(), + )); + + ensure!( + target_collection.confirm_sponsorship(&sponsor)?, + Error::::ConfirmUnsetSponsorFail + ); + + Self::deposit_event(Event::::SponsorshipConfirmed(collection_id, sponsor)); + + target_collection.save() + } + + pub fn force_remove_collection_sponsor(collection_id: CollectionId) -> DispatchResult { + let mut target_collection = >::try_get(collection_id)?; + target_collection.check_is_internal()?; + target_collection.sponsorship = SponsorshipState::Disabled; + + Self::deposit_event(Event::::CollectionSponsorRemoved(collection_id)); + + target_collection.save() + } +} --- /dev/null +++ b/primitives/app_promotion_rpc/CHANGELOG.md @@ -0,0 +1,5 @@ +# Change Log + +All notable changes to this project will be documented in this file. + + --- /dev/null +++ b/primitives/app_promotion_rpc/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "app-promotion-rpc" +version = "0.1.0" +license = "GPLv3" +edition = "2021" + +[dependencies] +pallet-common = { default-features = false, path = '../../pallets/common' } +up-data-structs = { default-features = false, path = '../data-structs' } +codec = { package = "parity-scale-codec", version = "3.1.2", default-features = false, features = [ + "derive", +] } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +sp-api = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-core/std", + "sp-std/std", + "sp-api/std", + "sp-runtime/std", + "pallet-common/std", + "up-data-structs/std", +] --- /dev/null +++ b/primitives/app_promotion_rpc/src/lib.rs @@ -0,0 +1,41 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use sp_std::vec::Vec; +use codec::Decode; +use sp_runtime::{ + DispatchError, + traits::{AtLeast32BitUnsigned, Member}, +}; + +type Result = core::result::Result; + +sp_api::decl_runtime_apis! { + #[api_version(2)] + /// Trait for generate rpc. + pub trait AppPromotionApi where + BlockNumber: Decode + Member + AtLeast32BitUnsigned, + AccountId: Decode, + CrossAccountId: pallet_evm::account::CrossAccountId, + { + fn total_staked(staker: Option) -> Result; + fn total_staked_per_block(staker: CrossAccountId) -> Result>; + fn pending_unstake(staker: Option) -> Result; + fn pending_unstake_per_block(staker: CrossAccountId) -> Result>; + } +} --- a/primitives/common/src/constants.rs +++ b/primitives/common/src/constants.rs @@ -22,6 +22,7 @@ use crate::types::{BlockNumber, Balance}; pub const MILLISECS_PER_BLOCK: u64 = 12000; +pub const MILLISECS_PER_RELAY_BLOCK: u64 = 6000; pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; @@ -30,6 +31,11 @@ pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; +// These time units are defined in number of relay blocks. +pub const RELAY_MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_RELAY_BLOCK as BlockNumber); +pub const RELAY_HOURS: BlockNumber = RELAY_MINUTES * 60; +pub const RELAY_DAYS: BlockNumber = RELAY_HOURS * 24; + pub const MICROUNIQUE: Balance = 1_000_000_000_000; pub const MILLIUNIQUE: Balance = 1_000 * MICROUNIQUE; pub const CENTIUNIQUE: Balance = 10 * MILLIUNIQUE; --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -20,9 +20,13 @@ CollectionId, TokenId, RpcCollection, CollectionStats, CollectionLimits, Property, PropertyKeyPermission, TokenData, TokenChild, }; + use sp_std::vec::Vec; use codec::Decode; -use sp_runtime::DispatchError; +use sp_runtime::{ + DispatchError, + traits::{AtLeast32BitUnsigned, Member}, +}; type Result = core::result::Result; @@ -120,6 +124,7 @@ /// Get total pieces of token. fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Result>; + fn token_owners(collection: CollectionId, token: TokenId) -> Result>; } } --- 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; } --- /dev/null +++ b/runtime/common/config/pallets/app_promotion.rs @@ -0,0 +1,63 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +use crate::{ + runtime_common::config::pallets::{TreasuryAccountId, RelayChainBlockNumberProvider}, + Runtime, Balances, BlockNumber, Unique, Event, EvmContractHelpers, +}; + +use frame_support::{parameter_types, PalletId}; +use sp_arithmetic::Perbill; +use up_common::{ + constants::{UNIQUE, RELAY_DAYS}, + types::Balance, +}; + +#[cfg(all(not(feature = "unique-runtime"), not(feature = "quartz-runtime")))] +parameter_types! { + pub const AppPromotionId: PalletId = PalletId(*b"appstake"); + pub const RecalculationInterval: BlockNumber = 20; + pub const PendingInterval: BlockNumber = 10; + pub const Nominal: Balance = UNIQUE; + // pub const Day: BlockNumber = DAYS; + pub IntervalIncome: Perbill = Perbill::from_rational(RecalculationInterval::get(), RELAY_DAYS) * Perbill::from_rational(5u32, 10_000); +} + +#[cfg(any(feature = "unique-runtime", feature = "quartz-runtime"))] +parameter_types! { + pub const AppPromotionId: PalletId = PalletId(*b"appstake"); + pub const RecalculationInterval: BlockNumber = RELAY_DAYS; + pub const PendingInterval: BlockNumber = 7 * RELAY_DAYS; + pub const Nominal: Balance = UNIQUE; + // pub const Day: BlockNumber = RELAY_DAYS; + pub IntervalIncome: Perbill = Perbill::from_rational(5u32, 10_000); +} + +impl pallet_app_promotion::Config for Runtime { + type PalletId = AppPromotionId; + type CollectionHandler = Unique; + type ContractHandler = EvmContractHelpers; + type Currency = Balances; + type WeightInfo = pallet_app_promotion::weights::SubstrateWeight; + type TreasuryAccountId = TreasuryAccountId; + type RelayBlockNumberProvider = RelayChainBlockNumberProvider; + type RecalculationInterval = RecalculationInterval; + type PendingInterval = PendingInterval; + // type Day = Day; + type Nominal = Nominal; + type IntervalIncome = IntervalIncome; + type Event = Event; +} --- a/runtime/common/config/pallets/mod.rs +++ b/runtime/common/config/pallets/mod.rs @@ -40,6 +40,9 @@ #[cfg(feature = "scheduler")] pub mod scheduler; +#[cfg(feature = "app-promotion")] +pub mod app_promotion; + parameter_types! { pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account_truncating(); pub const CollectionCreationPrice: Balance = 2 * UNIQUE; --- a/runtime/common/construct_runtime/mod.rs +++ b/runtime/common/construct_runtime/mod.rs @@ -77,12 +77,15 @@ #[runtimes(opal)] RmrkEquip: pallet_proxy_rmrk_equip::{Pallet, Call, Storage, Event} = 72, + #[runtimes(opal)] + AppPromotion: pallet_app_promotion::{Pallet, Call, Storage, Event} = 73, + // Frontier EVM: pallet_evm::{Pallet, Config, Call, Storage, Event} = 100, 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} = 151, EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152, EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153, } --- a/runtime/common/runtime_apis.rs +++ b/runtime/common/runtime_apis.rs @@ -189,6 +189,40 @@ } } + impl app_promotion_rpc::AppPromotionApi for Runtime { + fn total_staked(staker: Option) -> Result { + #[cfg(not(feature = "app-promotion"))] + return unsupported!(); + + #[cfg(feature = "app-promotion")] + return Ok(>::cross_id_total_staked(staker).unwrap_or_default()); + } + + fn total_staked_per_block(staker: CrossAccountId) -> Result, DispatchError> { + #[cfg(not(feature = "app-promotion"))] + return unsupported!(); + + #[cfg(feature = "app-promotion")] + return Ok(>::cross_id_total_staked_per_block(staker)); + } + + fn pending_unstake(staker: Option) -> Result { + #[cfg(not(feature = "app-promotion"))] + return unsupported!(); + + #[cfg(feature = "app-promotion")] + return Ok(>::cross_id_pending_unstake(staker)); + } + + fn pending_unstake_per_block(staker: CrossAccountId) -> Result, DispatchError> { + #[cfg(not(feature = "app-promotion"))] + return unsupported!(); + + #[cfg(feature = "app-promotion")] + return Ok(>::cross_id_pending_unstake_per_block(staker)) + } + } + impl rmrk_rpc::RmrkApi< Block, AccountId, @@ -638,6 +672,7 @@ list_benchmark!(list, extra, pallet_unique, Unique); list_benchmark!(list, extra, pallet_structure, Structure); list_benchmark!(list, extra, pallet_inflation, Inflation); + list_benchmark!(list, extra, pallet_app_promotion, AppPromotion); list_benchmark!(list, extra, pallet_fungible, Fungible); list_benchmark!(list, extra, pallet_nonfungible, Nonfungible); @@ -693,6 +728,7 @@ add_benchmark!(params, batches, pallet_unique, Unique); add_benchmark!(params, batches, pallet_structure, Structure); add_benchmark!(params, batches, pallet_inflation, Inflation); + add_benchmark!(params, batches, pallet_app_promotion, AppPromotion); add_benchmark!(params, batches, pallet_fungible, Fungible); add_benchmark!(params, batches, pallet_nonfungible, Nonfungible); --- a/runtime/opal/Cargo.toml +++ b/runtime/opal/Cargo.toml @@ -37,6 +37,7 @@ 'pallet-proxy-rmrk-equip/runtime-benchmarks', 'pallet-unique/runtime-benchmarks', 'pallet-inflation/runtime-benchmarks', + 'pallet-app-promotion/runtime-benchmarks', 'pallet-unique-scheduler/runtime-benchmarks', 'pallet-xcm/runtime-benchmarks', 'sp-runtime/runtime-benchmarks', @@ -82,12 +83,14 @@ 'pallet-base-fee/std', 'fp-rpc/std', 'up-rpc/std', + 'app-promotion-rpc/std', 'fp-evm-mapping/std', 'fp-self-contained/std', 'parachain-info/std', 'serde', 'pallet-inflation/std', 'pallet-configuration/std', + 'pallet-app-promotion/std', 'pallet-common/std', 'pallet-structure/std', 'pallet-fungible/std', @@ -122,11 +125,12 @@ "orml-vesting/std", ] limit-testing = ['pallet-unique/limit-testing', 'up-data-structs/limit-testing'] -opal-runtime = ['refungible', 'scheduler', 'rmrk'] +opal-runtime = ['refungible', 'scheduler', 'rmrk', 'app-promotion'] refungible = [] scheduler = [] rmrk = [] +app-promotion = [] ################################################################################ # Substrate Dependencies @@ -411,9 +415,11 @@ derivative = "2.2.0" pallet-unique = { path = '../../pallets/unique', default-features = false } up-rpc = { path = "../../primitives/rpc", default-features = false } +app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false} rmrk-rpc = { path = "../../primitives/rmrk-rpc", default-features = false } fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } pallet-inflation = { path = '../../pallets/inflation', default-features = false } +pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false } up-data-structs = { path = '../../primitives/data-structs', default-features = false } pallet-configuration = { default-features = false, path = "../../pallets/configuration" } pallet-common = { default-features = false, path = "../../pallets/common" } --- a/runtime/quartz/Cargo.toml +++ b/runtime/quartz/Cargo.toml @@ -82,6 +82,7 @@ 'pallet-base-fee/std', 'fp-rpc/std', 'up-rpc/std', + 'app-promotion-rpc/std', 'fp-evm-mapping/std', 'fp-self-contained/std', 'parachain-info/std', @@ -416,8 +417,10 @@ derivative = "2.2.0" pallet-unique = { path = '../../pallets/unique', default-features = false } up-rpc = { path = "../../primitives/rpc", default-features = false } +app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false} fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } pallet-inflation = { path = '../../pallets/inflation', default-features = false } +pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false } up-data-structs = { path = '../../primitives/data-structs', default-features = false } pallet-configuration = { default-features = false, path = "../../pallets/configuration" } pallet-common = { default-features = false, path = "../../pallets/common" } --- a/runtime/unique/Cargo.toml +++ b/runtime/unique/Cargo.toml @@ -83,6 +83,7 @@ 'pallet-base-fee/std', 'fp-rpc/std', 'up-rpc/std', + 'app-promotion-rpc/std', 'fp-evm-mapping/std', 'fp-self-contained/std', 'parachain-info/std', @@ -409,8 +410,10 @@ derivative = "2.2.0" pallet-unique = { path = '../../pallets/unique', default-features = false } up-rpc = { path = "../../primitives/rpc", default-features = false } +app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false} rmrk-rpc = { path = "../../primitives/rmrk-rpc", default-features = false } pallet-inflation = { path = '../../pallets/inflation', default-features = false } +pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false } up-data-structs = { path = '../../primitives/data-structs', default-features = false } pallet-configuration = { default-features = false, path = "../../pallets/configuration" } pallet-common = { default-features = false, path = "../../pallets/common" } --- a/tests/package.json +++ b/tests/package.json @@ -30,6 +30,7 @@ "testEth": "mocha --timeout 9999999 -r ts-node/register './**/eth/**/*.test.ts'", "testEthMarketplace": "mocha --timeout 9999999 -r ts-node/register './**/eth/marketplace/**/*.test.ts'", "testEthNesting": "mocha --timeout 9999999 -r ts-node/register './**/eth/nesting/**/*.test.ts'", + "testEthPayable": "mocha --timeout 9999999 -r ts-node/register './**/eth/payable.test.ts'", "load": "mocha --timeout 9999999 -r ts-node/register './**/*.load.ts'", "loadTransfer": "ts-node src/transfer.nload.ts", "testCollision": "mocha --timeout 9999999 -r ts-node/register ./src/collision-tests/*.test.ts", @@ -64,6 +65,7 @@ "testBurnItem": "mocha --timeout 9999999 -r ts-node/register ./**/burnItem.test.ts", "testAdminTransferAndBurn": "mocha --timeout 9999999 -r ts-node/register ./**/adminTransferAndBurn.test.ts", "testSetMintPermission": "mocha --timeout 9999999 -r ts-node/register ./**/setMintPermission.test.ts", + "testSetPublicAccessMode": "mocha --timeout 9999999 -r ts-node/register ./**/setPublicAccessMode.test.ts", "testCreditFeesToTreasury": "mocha --timeout 9999999 -r ts-node/register ./**/creditFeesToTreasury.test.ts", "testContractSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/contractSponsoring.test.ts", "testEnableContractSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/enableContractSponsoring.test.ts", @@ -71,7 +73,6 @@ "testSetContractSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setContractSponsoringRateLimit.test.ts", "testSetOffchainSchema": "mocha --timeout 9999999 -r ts-node/register ./**/setOffchainSchema.test.ts", "testOverflow": "mocha --timeout 9999999 -r ts-node/register ./**/overflow.test.ts", - "testSetVariableMetadataSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetadataSponsoringRateLimit.test.ts", "testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts", "testScheduler": "mocha --timeout 9999999 -r ts-node/register ./**/scheduler.test.ts", "testSchedulingEVM": "mocha --timeout 9999999 -r ts-node/register ./**/eth/scheduling.test.ts", @@ -80,10 +81,12 @@ "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts", "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts", "testLimits": "mocha --timeout 9999999 -r ts-node/register ./**/limits.test.ts", - "testEthCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createCollection.test.ts", + "testEthCreateNFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createNFTCollection.test.ts", + "testEthCreateRFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createRFTCollection.test.ts", "testRFT": "mocha --timeout 9999999 -r ts-node/register ./**/refungible.test.ts", "testFT": "mocha --timeout 9999999 -r ts-node/register ./**/fungible.test.ts", "testRPC": "mocha --timeout 9999999 -r ts-node/register ./**/rpc.test.ts", + "testPromotion": "mocha --timeout 9999999 -r ts-node/register ./**/app-promotion.test.ts", "polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json", "polkadot-types-from-defs": "ts-node ./node_modules/.bin/polkadot-types-from-defs --endpoint src/interfaces/metadata.json --input src/interfaces/ --package .", "polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint src/interfaces/metadata.json --output src/interfaces/ --package .", --- /dev/null +++ b/tests/src/app-promotion.test.ts @@ -0,0 +1,800 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {IKeyringPair} from '@polkadot/types/types'; +import { + normalizeAccountId, + getModuleNames, + Pallets, +} from './util/helpers'; + +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import {usingPlaygrounds} from './util/playgrounds'; + +import {encodeAddress} from '@polkadot/util-crypto'; +import {stringToU8a} from '@polkadot/util'; +import {SponsoringMode, contractHelpers, createEthAccountWithBalance, deployFlipper, itWeb3, transferBalanceToEth} from './eth/util/helpers'; +import {DevUniqueHelper} from './util/playgrounds/unique.dev'; +chai.use(chaiAsPromised); +const expect = chai.expect; + +let alice: IKeyringPair; +let palletAdmin: IKeyringPair; +let nominal: bigint; +const palletAddress = calculatePalleteAddress('appstake'); +let accounts: IKeyringPair[] = []; +const LOCKING_PERIOD = 20n; // 20 blocks of relay +const UNLOCKING_PERIOD = 10n; // 20 blocks of parachain +const rewardAvailableInBlock = (stakedInBlock: bigint) => (stakedInBlock - stakedInBlock % LOCKING_PERIOD) + (LOCKING_PERIOD * 2n); + +const beforeEach = async (context: Mocha.Context) => { + await usingPlaygrounds(async (helper, privateKey) => { + if (!getModuleNames(helper.api!).includes(Pallets.AppPromotion)) context.skip(); + alice = privateKey('//Alice'); + palletAdmin = privateKey('//Charlie'); // TODO use custom address + await helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address}))); + nominal = helper.balance.getOneTokenNominal(); + await helper.balance.transferToSubstrate(alice, palletAdmin.address, 1000n * nominal); + await helper.balance.transferToSubstrate(alice, palletAddress, 1000n * nominal); + accounts = await helper.arrange.createCrowd(100, 1000n, alice); // create accounts-pool to speed up tests + }); +}; + +describe('app-promotions.stake extrinsic', () => { + before(async function () { + await beforeEach(this); + }); + + it('should "lock" staking balance, add it to "staked" map, and increase "totalStaked" amount', async () => { + await usingPlaygrounds(async (helper) => { + const [staker, recepient] = [accounts.pop()!, accounts.pop()!]; + const totalStakedBefore = await helper.staking.getTotalStaked(); + + // Minimum stake amount is 100: + await expect(helper.staking.stake(staker, 100n * nominal - 1n)).to.be.eventually.rejected; + await helper.staking.stake(staker, 100n * nominal); + + // Staker balance is: miscFrozen: 100, feeFrozen: 100, reserved: 0n... + // ...so he can not transfer 900 + expect (await helper.balance.getSubstrateFull(staker.address)).to.contain({miscFrozen: 100n * nominal, feeFrozen: 100n * nominal, reserved: 0n}); + await expect(helper.balance.transferToSubstrate(staker, recepient.address, 900n * nominal)).to.be.rejected; + + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(100n * nominal); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); + // it is potentially flaky test. Promotion can credited some tokens. Maybe we need to use closeTo? + expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore + 100n * nominal); // total tokens amount staked in app-promotion increased + + + await helper.staking.stake(staker, 200n * nominal); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(300n * nominal); + expect((await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map((x) => x[1])).to.be.deep.equal([100n * nominal, 200n * nominal]); + }); + }); + + it('should allow to create maximum 10 stakes for account', async () => { + await usingPlaygrounds(async (helper) => { + const [staker] = await helper.arrange.createAccounts([2000n], alice); + for (let i = 0; i < 10; i++) { + await helper.staking.stake(staker, 100n * nominal); + } + + // can have 10 stakes + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(1000n * nominal); + expect(await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).to.have.length(10); + + await expect(helper.staking.stake(staker, 100n * nominal)).to.be.rejected; + + // After unstake can stake again + await helper.staking.unstake(staker); + await helper.staking.stake(staker, 100n * nominal); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(100n * nominal); + }); + }); + + it('should reject transaction if stake amount is more than total free balance minus frozen', async () => { + await usingPlaygrounds(async helper => { + const staker = accounts.pop()!; + + // Can't stake full balance because Alice needs to pay some fee + await expect(helper.staking.stake(staker, 1000n * nominal)).to.be.eventually.rejected; + await helper.staking.stake(staker, 500n * nominal); + + // Can't stake 500 tkn because Alice has Less than 500 transferable; + await expect(helper.staking.stake(staker, 500n * nominal)).to.be.eventually.rejected; + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(500n * nominal); + }); + }); + + it('for different accounts in one block is possible', async () => { + await usingPlaygrounds(async helper => { + const crowd = [accounts.pop()!, accounts.pop()!, accounts.pop()!, accounts.pop()!]; + + const crowdStartsToStake = crowd.map(user => helper.staking.stake(user, 100n * nominal)); + await expect(Promise.all(crowdStartsToStake)).to.be.eventually.fulfilled; + + const crowdStakes = await Promise.all(crowd.map(address => helper.staking.getTotalStaked({Substrate: address.address}))); + expect(crowdStakes).to.deep.equal([100n * nominal, 100n * nominal, 100n * nominal, 100n * nominal]); + }); + }); +}); + +describe('unstake balance extrinsic', () => { + before(async function () { + await beforeEach(this); + }); + + it('should change balance state from "frozen" to "reserved", add it to "pendingUnstake" map, and subtract it from totalStaked', async () => { + await usingPlaygrounds(async helper => { + const [staker, recepient] = [accounts.pop()!, accounts.pop()!]; + const totalStakedBefore = await helper.staking.getTotalStaked(); + await helper.staking.stake(staker, 900n * nominal); + await helper.staking.unstake(staker); + + // Right after unstake balance is reserved + // Staker can not transfer + expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 900n * nominal, miscFrozen: 0n, feeFrozen: 0n}); + await expect(helper.balance.transferToSubstrate(staker, recepient.address, 100n * nominal)).to.be.rejected; + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(900n * nominal); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); + expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore); + }); + }); + + it('should unlock balance after unlocking period ends and remove it from "pendingUnstake"', async () => { + await usingPlaygrounds(async (helper) => { + const staker = accounts.pop()!; + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.unstake(staker); + const unstakedInBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}))[0][0]; + + // Wait for unstaking period. Balance now free ~1000; reserved, frozen, miscFrozeb: 0n + await helper.wait.forParachainBlockNumber(unstakedInBlock); + expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, miscFrozen: 0n, feeFrozen: 0n}); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); + + // staker can transfer: + await helper.balance.transferToSubstrate(staker, alice.address, 998n * nominal); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(1n); + }); + }); + + it('should successfully unstake multiple stakes', async () => { + await usingPlaygrounds(async helper => { + const staker = accounts.pop()!; + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.stake(staker, 200n * nominal); + await helper.staking.stake(staker, 300n * nominal); + + // staked: [100, 200, 300]; unstaked: 0 + let pendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address}); + let unstakedPerBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address})).map(stake => stake[1]); + let stakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(stake => stake[1]); + expect(pendingUnstake).to.be.deep.equal(0n); + expect(unstakedPerBlock).to.be.deep.equal([]); + expect(stakedPerBlock).to.be.deep.equal([100n * nominal, 200n * nominal, 300n * nominal]); + + // Can unstake multiple stakes + await helper.staking.unstake(staker); + const unstakingBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}))[0][0]; + pendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address}); + unstakedPerBlock = (await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address})).map(stake => stake[1]); + stakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(stake => stake[1]); + expect(pendingUnstake).to.be.equal(600n * nominal); + expect(stakedPerBlock).to.be.deep.equal([]); + expect(unstakedPerBlock).to.be.deep.equal([600n * nominal]); + + expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 600n * nominal, feeFrozen: 0n, miscFrozen: 0n}); + await helper.wait.forParachainBlockNumber(unstakingBlock); + expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, feeFrozen: 0n, miscFrozen: 0n}); + expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); + }); + }); + + it('should not have any effects if no active stakes', async () => { + await usingPlaygrounds(async (helper) => { + const staker = accounts.pop()!; + + // unstake has no effect if no stakes at all + await helper.staking.unstake(staker); + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(0n); + expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // TODO bigint closeTo helper + + // TODO stake() unstake() waitUnstaked() unstake(); + + // can't unstake if there are only pendingUnstakes + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.unstake(staker); + await helper.staking.unstake(staker); + + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); + }); + }); + + it('should keep different unlocking block for each unlocking stake', async () => { + await usingPlaygrounds(async (helper) => { + const staker = accounts.pop()!; + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.unstake(staker); + await helper.staking.stake(staker, 120n * nominal); + await helper.staking.unstake(staker); + + const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address}); + expect(unstakingPerBlock).has.length(2); + expect(unstakingPerBlock[0][1]).to.equal(100n * nominal); + expect(unstakingPerBlock[1][1]).to.equal(120n * nominal); + }); + }); + + it('should be possible for different accounts in one block', async () => { + await usingPlaygrounds(async (helper) => { + const stakers = [accounts.pop()!, accounts.pop()!, accounts.pop()!]; + + await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal))); + await Promise.all(stakers.map(staker => helper.staking.unstake(staker))); + + await Promise.all(stakers.map(async (staker) => { + expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal); + expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n); + })); + }); + }); +}); + +describe('Admin adress', () => { + before(async function () { + await beforeEach(this); + }); + + it('can be set by sudo only', async () => { + await usingPlaygrounds(async (helper) => { + const nonAdmin = accounts.pop()!; + // nonAdmin can not set admin not from himself nor as a sudo + await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address}))).to.be.eventually.rejected; + await expect(helper.signTransaction(nonAdmin, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address})))).to.be.eventually.rejected; + + // Alice can + await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.eventually.fulfilled; + }); + }); + + it('can be any valid CrossAccountId', async () => { + // We are not going to set an eth address as a sponsor, + // but we do want to check, it doesn't break anything; + await usingPlaygrounds(async (helper) => { + const account = accounts.pop()!; + const ethAccount = helper.address.substrateToEth(account.address); + // Alice sets Ethereum address as a sudo. Then Substrate address back... + await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Ethereum: ethAccount})))).to.be.eventually.fulfilled; + await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.eventually.fulfilled; + + // ...It doesn't break anything; + const collection = await helper.nft.mintCollection(account, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + await expect(helper.signTransaction(account, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.rejected; + }); + }); + + it('can be reassigned', async () => { + await usingPlaygrounds(async (helper) => { + const [oldAdmin, newAdmin, collectionOwner] = [accounts.pop()!, accounts.pop()!, accounts.pop()!]; + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + + await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress(normalizeAccountId(oldAdmin))))).to.be.eventually.fulfilled; + await expect(helper.signTransaction(alice, helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress(normalizeAccountId(newAdmin))))).to.be.eventually.fulfilled; + await expect(helper.signTransaction(oldAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.rejected; + + await expect(helper.signTransaction(newAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.fulfilled; + }); + }); +}); + +describe('App-promotion collection sponsoring', () => { + before(async function () { + await beforeEach(this); + await usingPlaygrounds(async (helper) => { + const tx = helper.api!.tx.sudo.sudo(helper.api!.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})); + await helper.signTransaction(alice, tx); + }); + }); + + it('should actually sponsor transactions', async () => { + await usingPlaygrounds(async (helper) => { + const [collectionOwner, tokenSender, receiver] = [accounts.pop()!, accounts.pop()!, accounts.pop()!]; + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'Name', description: 'Description', tokenPrefix: 'Prefix', limits: {sponsorTransferTimeout: 0}}); + const token = await collection.mintToken(collectionOwner, {Substrate: tokenSender.address}); + await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId)); + const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress); + + await token.transfer(tokenSender, {Substrate: receiver.address}); + expect (await token.getOwner()).to.be.deep.equal({Substrate: receiver.address}); + const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress); + + // senders balance the same, transaction has sponsored + expect (await helper.balance.getSubstrate(tokenSender.address)).to.be.equal(1000n * nominal); + expect (palletBalanceBefore > palletBalanceAfter).to.be.true; + }); + }); + + it('can not be set by non admin', async () => { + await usingPlaygrounds(async (helper) => { + const [collectionOwner, nonAdmin] = [accounts.pop()!, accounts.pop()!]; + + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + + await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.rejected; + expect((await collection.getData())?.raw.sponsorship).to.equal('Disabled'); + }); + }); + + it('should set pallet address as confirmed admin', async () => { + await usingPlaygrounds(async (helper) => { + const [collectionOwner, oldSponsor] = [accounts.pop()!, accounts.pop()!]; + + // Can set sponsoring for collection without sponsor + const collectionWithoutSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'No-sponsor', description: 'New Collection', tokenPrefix: 'Promotion'}); + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithoutSponsor.collectionId))).to.be.eventually.fulfilled; + expect((await collectionWithoutSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); + + // Can set sponsoring for collection with unconfirmed sponsor + const collectionWithUnconfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Unconfirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: oldSponsor.address}); + expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: oldSponsor.address}); + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithUnconfirmedSponsor.collectionId))).to.be.eventually.fulfilled; + expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); + + // Can set sponsoring for collection with confirmed sponsor + const collectionWithConfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Confirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: oldSponsor.address}); + await collectionWithConfirmedSponsor.confirmSponsorship(oldSponsor); + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithConfirmedSponsor.collectionId))).to.be.eventually.fulfilled; + expect((await collectionWithConfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); + }); + }); + + it('can be overwritten by collection owner', async () => { + await usingPlaygrounds(async (helper) => { + const [collectionOwner, newSponsor] = [accounts.pop()!, accounts.pop()!]; + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + const collectionId = collection.collectionId; + + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionId))).to.be.eventually.fulfilled; + + // Collection limits still can be changed by the owner + expect(await collection.setLimits(collectionOwner, {sponsorTransferTimeout: 0})).to.be.true; + expect((await collection.getData())?.raw.limits.sponsorTransferTimeout).to.be.equal(0); + expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); + + // Collection sponsor can be changed too + expect((await collection.setSponsor(collectionOwner, newSponsor.address))).to.be.true; + expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: newSponsor.address}); + }); + }); + + it('should not overwrite collection limits set by the owner earlier', async () => { + await usingPlaygrounds(async (helper) => { + const limits = {ownerCanDestroy: true, ownerCanTransfer: true, sponsorTransferTimeout: 0}; + const collectionWithLimits = await helper.nft.mintCollection(alice, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits}); + + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collectionWithLimits.collectionId))).to.be.eventually.fulfilled; + expect((await collectionWithLimits.getData())?.raw.limits).to.be.deep.contain(limits); + }); + }); + + it('should reject transaction if collection doesn\'t exist', async () => { + await usingPlaygrounds(async (helper) => { + const collectionOwner = accounts.pop()!; + + // collection has never existed + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(999999999))).to.be.eventually.rejected; + // collection has been burned + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + await collection.burn(collectionOwner); + + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.rejected; + }); + }); +}); + +describe('app-promotion stopSponsoringCollection', () => { + before(async function () { + await beforeEach(this); + }); + + it('can not be called by non-admin', async () => { + await usingPlaygrounds(async (helper) => { + const [collectionOwner, nonAdmin] = [accounts.pop()!, accounts.pop()!]; + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.eventually.fulfilled; + + await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.eventually.rejected; + expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress}); + }); + }); + + it('should set sponsoring as disabled', async () => { + await usingPlaygrounds(async (helper) => { + const [collectionOwner, recepient] = [accounts.pop()!, accounts.pop()!]; + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits: {sponsorTransferTimeout: 0}}); + const token = await collection.mintToken(collectionOwner, {Substrate: collectionOwner.address}); + + await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.sponsorCollection(collection.collectionId)); + await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(collection.collectionId)); + + expect((await collection.getData())?.raw.sponsorship).to.be.equal('Disabled'); + + // Transactions are not sponsored anymore: + const ownerBalanceBefore = await helper.balance.getSubstrate(collectionOwner.address); + await token.transfer(collectionOwner, {Substrate: recepient.address}); + const ownerBalanceAfter = await helper.balance.getSubstrate(collectionOwner.address); + expect(ownerBalanceAfter < ownerBalanceBefore).to.be.equal(true); + }); + }); + + it('should not affect collection which is not sponsored by pallete', async () => { + await usingPlaygrounds(async (helper) => { + const collectionOwner = accounts.pop()!; + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: collectionOwner.address}); + await collection.confirmSponsorship(collectionOwner); + + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.eventually.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: collectionOwner.address}); + }); + }); + + it('should reject transaction if collection does not exist', async () => { + await usingPlaygrounds(async (helper) => { + const collectionOwner = accounts.pop()!; + const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'}); + + await collection.burn(collectionOwner); + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.eventually.rejected; + await expect(helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.stopSponsoringCollection(999999999))).to.be.eventually.rejected; + }); + }); +}); + +describe('app-promotion contract sponsoring', () => { + before(async function () { + await beforeEach(this); + }); + + itWeb3('should set palletes address as a sponsor', async ({api, web3, privateKeyWrapper}) => { + await usingPlaygrounds(async (helper) => { + const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); + const flipper = await deployFlipper(web3, contractOwner); + const contractMethods = contractHelpers(web3, contractOwner); + + await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); + + expect(await contractMethods.methods.hasSponsor(flipper.options.address).call()).to.be.true; + expect((await api.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); + expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ + confirmed: { + substrate: palletAddress, + }, + }); + }); + }); + + itWeb3('should overwrite sponsoring mode and existed sponsor', async ({api, web3, privateKeyWrapper}) => { + await usingPlaygrounds(async (helper) => { + const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); + const flipper = await deployFlipper(web3, contractOwner); + const contractMethods = contractHelpers(web3, contractOwner); + + await expect(contractMethods.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; + + // Contract is self sponsored + expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.be.deep.equal({ + confirmed: { + ethereum: flipper.options.address.toLowerCase(), + }, + }); + + // set promotion sponsoring + await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); + + // new sponsor is pallet address + expect(await contractMethods.methods.hasSponsor(flipper.options.address).call()).to.be.true; + expect((await api.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); + expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ + confirmed: { + substrate: palletAddress, + }, + }); + }); + }); + + itWeb3('can be overwritten by contract owner', async ({api, web3, privateKeyWrapper}) => { + await usingPlaygrounds(async (helper) => { + const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); + const flipper = await deployFlipper(web3, contractOwner); + const contractMethods = contractHelpers(web3, contractOwner); + + // contract sponsored by pallet + await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); + + // owner sets self sponsoring + await expect(contractMethods.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; + + expect(await contractMethods.methods.hasSponsor(flipper.options.address).call()).to.be.true; + expect((await api.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); + expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ + confirmed: { + ethereum: flipper.options.address.toLowerCase(), + }, + }); + }); + }); + + itWeb3('can not be set by non admin', async ({api, web3, privateKeyWrapper}) => { + await usingPlaygrounds(async (helper) => { + const nonAdmin = accounts.pop()!; + const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); + const flipper = await deployFlipper(web3, contractOwner); + const contractMethods = contractHelpers(web3, contractOwner); + + await expect(contractMethods.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; + + // nonAdmin calls sponsorContract + await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address))).to.be.rejected; + + // contract still self-sponsored + expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ + confirmed: { + ethereum: flipper.options.address.toLowerCase(), + }, + }); + }); + + itWeb3('should be rejected for non-contract address', async ({api, web3, privateKeyWrapper}) => { + await usingPlaygrounds(async (helper) => { + + }); + }); + }); + + itWeb3('should actually sponsor transactions', async ({api, web3, privateKeyWrapper}) => { + await usingPlaygrounds(async (helper) => { + const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); + const flipper = await deployFlipper(web3, contractOwner); + const contractHelper = contractHelpers(web3, contractOwner); + await contractHelper.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: contractOwner}); + await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner}); + await transferBalanceToEth(api, alice, flipper.options.address, 1000n); + + await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.true; + + const callerBalance = await helper.balance.getEthereum(caller); + const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address); + + expect(callerBalance).to.be.equal(1000n * nominal); + expect(1000n * nominal > contractBalanceAfter).to.be.true; + }); + }); +}); + +describe('app-promotion stopSponsoringContract', () => { + before(async function () { + await beforeEach(this); + }); + + itWeb3('should remove pallet address from contract sponsors', async ({api, web3, privateKeyWrapper}) => { + await usingPlaygrounds(async (helper) => { + const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); + const flipper = await deployFlipper(web3, contractOwner); + await transferBalanceToEth(api, alice, flipper.options.address); + const contractHelper = contractHelpers(web3, contractOwner); + await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner}); + await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); + await helper.signTransaction(palletAdmin, api.tx.appPromotion.stopSponsoringContract(flipper.options.address)); + + expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.false; + expect((await api.query.evmContractHelpers.owner(flipper.options.address)).toJSON()).to.be.equal(contractOwner); + expect((await api.query.evmContractHelpers.sponsoring(flipper.options.address)).toJSON()).to.deep.equal({ + disabled: null, + }); + + await flipper.methods.flip().send({from: caller}); + expect(await flipper.methods.getValue().call()).to.be.true; + + const callerBalance = await helper.balance.getEthereum(caller); + const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address); + + // caller payed for call + expect(1000n * nominal > callerBalance).to.be.true; + expect(contractBalanceAfter).to.be.equal(1000n * nominal); + }); + }); + + itWeb3('can not be called by non-admin', async ({api, web3, privateKeyWrapper}) => { + await usingPlaygrounds(async (helper) => { + const nonAdmin = accounts.pop()!; + const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); + const flipper = await deployFlipper(web3, contractOwner); + + await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorContract(flipper.options.address)); + await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.stopSponsoringContract(flipper.options.address))).to.be.rejected; + }); + }); + + itWeb3('should not affect a contract which is not sponsored by pallete', async ({api, web3, privateKeyWrapper}) => { + await usingPlaygrounds(async (helper) => { + const nonAdmin = accounts.pop()!; + const contractOwner = (await createEthAccountWithBalance(api, web3, privateKeyWrapper)).toLowerCase(); + const flipper = await deployFlipper(web3, contractOwner); + const contractHelper = contractHelpers(web3, contractOwner); + await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected; + + await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.stopSponsoringContract(flipper.options.address))).to.be.rejected; + }); + }); +}); + +describe('app-promotion rewards', () => { + before(async function () { + await beforeEach(this); + }); + + it('can not be called by non admin', async () => { + await usingPlaygrounds(async (helper) => { + const nonAdmin = accounts.pop()!; + await expect(helper.signTransaction(nonAdmin, helper.api!.tx.appPromotion.payoutStakers(100))).to.be.rejected; + }); + }); + + it('should credit 0.05% for staking period', async () => { + await usingPlaygrounds(async helper => { + const staker = accounts.pop()!; + + await waitPromotionPeriodDoesntEnd(helper); + + await helper.staking.stake(staker, 100n * nominal); + await helper.staking.stake(staker, 200n * nominal); + + // wait rewards are available: + const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[1][0]; + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock)); + + await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); + + const totalStakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(s => s[1]); + expect(totalStakedPerBlock).to.be.deep.equal([calculateIncome(100n * nominal, 10n), calculateIncome(200n * nominal, 10n)]); + }); + }); + + it('shoud be paid for more than one period if payments was missed', async () => { + await usingPlaygrounds(async (helper) => { + const staker = accounts.pop()!; + + await helper.staking.stake(staker, 100n * nominal); + // wait for two rewards are available: + const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[0][0]; + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock) + LOCKING_PERIOD); + + await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); + const stakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}); + const frozenBalanceShouldBe = calculateIncome(100n * nominal, 10n, 2); + expect(stakedPerBlock[0][1]).to.be.equal(frozenBalanceShouldBe); + + const stakerFullBalance = await helper.balance.getSubstrateFull(staker.address); + + expect(stakerFullBalance).to.contain({reserved: 0n, feeFrozen: frozenBalanceShouldBe, miscFrozen: frozenBalanceShouldBe}); + }); + }); + + it('should not be credited for unstaked (reserved) balance', async () => { + await usingPlaygrounds(async helper => { + // staker unstakes before rewards has been payed + const staker = accounts.pop()!; + await helper.staking.stake(staker, 100n * nominal); + const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[0][0]; + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock) + LOCKING_PERIOD); + await helper.staking.unstake(staker); + + // so he did not receive any rewards + const totalBalanceBefore = await helper.balance.getSubstrate(staker.address); + await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); + const totalBalanceAfter = await helper.balance.getSubstrate(staker.address); + + expect(totalBalanceBefore).to.be.equal(totalBalanceAfter); + }); + }); + + it('should bring compound interest', async () => { + await usingPlaygrounds(async helper => { + const staker = accounts.pop()!; + + await helper.staking.stake(staker, 100n * nominal); + + const stakedInBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address}))[0][0]; + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock)); + + await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); + let totalStakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(s => s[1]); + expect(totalStakedPerBlock).to.deep.equal([calculateIncome(100n * nominal, 10n)]); + + await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock) + LOCKING_PERIOD); + await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); + totalStakedPerBlock = (await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).map(s => s[1]); + expect(totalStakedPerBlock).to.deep.equal([calculateIncome(100n * nominal, 10n, 2)]); + }); + }); + + it.skip('can be paid 1000 rewards in a time', async () => { + // all other stakes should be unstaked + await usingPlaygrounds(async (helper) => { + const oneHundredStakers = await helper.arrange.createCrowd(100, 1050n, alice); + + // stakers stakes 10 times each + for (let i = 0; i < 10; i++) { + await Promise.all(oneHundredStakers.map(staker => helper.staking.stake(staker, 100n * nominal))); + } + await helper.wait.newBlocks(40); + const result = await helper.signTransaction(palletAdmin, helper.api!.tx.appPromotion.payoutStakers(100)); + }); + }); + + it.skip('can handle 40.000 rewards', async () => { + await usingPlaygrounds(async (helper) => { + const [donor] = await helper.arrange.createAccounts([7_000_000n], alice); + const crowdStakes = async () => { + // each account in the crowd stakes 2 times + const crowd = await helper.arrange.createCrowd(500, 300n, donor); + await Promise.all(crowd.map(account => helper.staking.stake(account, 100n * nominal))); + await Promise.all(crowd.map(account => helper.staking.stake(account, 100n * nominal))); + // + }; + + for (let i = 0; i < 40; i++) { + await crowdStakes(); + } + + // TODO pay rewards for some period + }); + }); +}); + +function calculatePalleteAddress(palletId: any) { + const address = stringToU8a(('modl' + palletId).padEnd(32, '\0')); + return encodeAddress(address); +} + +function calculateIncome(base: bigint, calcPeriod: bigint, iter = 0): bigint { + const DAY = 7200n; + const ACCURACY = 1_000_000_000n; + const income = base + base * (ACCURACY * (calcPeriod * 5n) / (10_000n * DAY)) / ACCURACY ; + + if (iter > 1) { + return calculateIncome(income, calcPeriod, iter - 1); + } else return income; +} + +// Wait while promotion period less than specified block, to avoid boundary cases +// 0 if this should be the beginning of the period. +async function waitPromotionPeriodDoesntEnd(helper: DevUniqueHelper, waitBlockLessThan = LOCKING_PERIOD / 3n) { + const relayBlockNumber = (await helper.api!.query.parachainSystem.validationData()).value.relayParentNumber.toNumber(); // await helper.chain.getLatestBlockNumber(); + const currentPeriodBlock = BigInt(relayBlockNumber) % LOCKING_PERIOD; + + if (currentPeriodBlock > waitBlockLessThan) { + await helper.wait.forRelayBlockNumber(BigInt(relayBlockNumber) + LOCKING_PERIOD - currentPeriodBlock); + } +} --- a/tests/src/approve.test.ts +++ b/tests/src/approve.test.ts @@ -79,11 +79,12 @@ it('[nft] Remove approval by using 0 amount', async () => { await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const collectionId = collection.collectionId; const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, undefined, 0n); + await helper.api?.tx.unique.approve({Substrate: bob.address}, collectionId, tokenId, 0).signAndSend(alice); expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false; }); }); @@ -97,7 +98,7 @@ const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); expect(amountBefore).to.be.equal(BigInt(1)); - await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, undefined, 0n); + await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); expect(amountAfter).to.be.equal(BigInt(0)); }); @@ -111,7 +112,7 @@ const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); expect(amountBefore).to.be.equal(BigInt(100)); - await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, undefined, 0n); + await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); expect(amountAfter).to.be.equal(BigInt(0)); }); @@ -289,7 +290,7 @@ const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); await helper.ft.mintTokens(alice, collectionId, alice.address, 10n); const tokenId = await helper.ft.getLastTokenId(collectionId); - await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, undefined, 10n); + await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 10n); const charlieBefore = await helper.ft.getBalance(collectionId, {Substrate: charlie.address}); await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}, 2n); @@ -321,7 +322,7 @@ const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, undefined, 0n); + await helper.api?.tx.unique.approve({Substrate: bob.address}, collectionId, tokenId, 0).signAndSend(alice); expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false; const transferTokenFromTx = async () => helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: bob.address}); await expect(transferTokenFromTx()).to.be.rejected; @@ -337,7 +338,7 @@ const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); expect(amountBefore).to.be.equal(BigInt(1)); - await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, undefined, 0n); + await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); expect(amountAfter).to.be.equal(BigInt(0)); @@ -354,7 +355,7 @@ const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); expect(amountBefore).to.be.equal(BigInt(100)); - await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, undefined, 0n); + await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); expect(amountAfter).to.be.equal(BigInt(0)); @@ -379,8 +380,7 @@ await usingPlaygrounds(async (helper) => { const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); - const approveTx = async () => helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}, undefined, 2n); - await expect(approveTx()).to.be.rejected; + await helper.api?.tx.unique.approve({Substrate: charlie.address}, collectionId, tokenId, 2).signAndSend(bob); expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.false; }); }); @@ -390,7 +390,7 @@ const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); await helper.ft.mintTokens(alice, collectionId, alice.address, 10n); const tokenId = await helper.ft.getLastTokenId(collectionId); - const approveTx = async () => helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, undefined, 11n); + const approveTx = async () => helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 11n); await expect(approveTx()).to.be.rejected; }); }); @@ -399,7 +399,7 @@ await usingPlaygrounds(async (helper) => { const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); - const approveTx = async () => helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, undefined, 101n); + const approveTx = async () => helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 101n); await expect(approveTx()).to.be.rejected; }); }); @@ -658,9 +658,9 @@ const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); await helper.rft.transferToken(alice, collectionId, tokenId, {Substrate: bob.address}, 100n); - await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, undefined, 100n); + await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 100n); - const approveTx = async () => helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, undefined, 101n); + const approveTx = async () => helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 101n); await expect(approveTx()).to.be.rejected; }); }); @@ -672,8 +672,8 @@ const tokenId = await helper.ft.getLastTokenId(collectionId); await helper.ft.transferToken(alice, collectionId, tokenId, {Substrate: bob.address}, 10n); - await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, undefined, 10n); - const approveTx = async () => helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, undefined, 11n); + await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 10n); + const approveTx = async () => helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 11n); await expect(approveTx()).to.be.rejected; }); }); --- 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 --- a/tests/src/eth/collectionAdmin.test.ts +++ b/tests/src/eth/collectionAdmin.test.ts @@ -15,7 +15,7 @@ import {expect} from 'chai'; import privateKey from '../substrate/privateKey'; -import { UNIQUE } from '../util/helpers'; +import {UNIQUE} from '../util/helpers'; import { createEthAccount, createEthAccountWithBalance, @@ -24,7 +24,6 @@ getCollectionAddressFromResult, itWeb3, recordEthFee, - subToEth, } from './util/helpers'; describe('Add collection admins', () => { @@ -37,7 +36,7 @@ .send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); - const newAdmin = await createEthAccount(web3); + const newAdmin = createEthAccount(web3); const collectionEvm = evmCollection(web3, owner, collectionIdAddress); await collectionEvm.methods.addCollectionAdmin(newAdmin).send(); const adminList = await api.rpc.unique.adminlist(collectionId); @@ -92,7 +91,7 @@ const collectionEvm = evmCollection(web3, owner, collectionIdAddress); await collectionEvm.methods.addCollectionAdmin(admin).send(); - const user = await createEthAccount(web3); + const user = createEthAccount(web3); await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: admin})) .to.be.rejectedWith('NoPermission'); @@ -114,7 +113,7 @@ const notAdmin = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const collectionEvm = evmCollection(web3, owner, collectionIdAddress); - const user = await createEthAccount(web3); + const user = createEthAccount(web3); await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: notAdmin})) .to.be.rejectedWith('NoPermission'); @@ -175,7 +174,7 @@ .send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); - const newAdmin = await createEthAccount(web3); + const newAdmin = createEthAccount(web3); const collectionEvm = evmCollection(web3, owner, collectionIdAddress); await collectionEvm.methods.addCollectionAdmin(newAdmin).send(); { @@ -226,7 +225,7 @@ const admin0 = await createEthAccountWithBalance(api, web3, privateKeyWrapper); await collectionEvm.methods.addCollectionAdmin(admin0).send(); - const admin1 = await createEthAccount(web3); + const admin1 = createEthAccount(web3); await collectionEvm.methods.addCollectionAdmin(admin1).send(); await expect(collectionEvm.methods.removeCollectionAdmin(admin1).call({from: admin0})) @@ -253,7 +252,7 @@ const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper); await collectionEvm.methods.addCollectionAdmin(admin).send(); - const notAdmin = await createEthAccount(web3); + const notAdmin = createEthAccount(web3); await expect(collectionEvm.methods.removeCollectionAdmin(admin).call({from: notAdmin})) .to.be.rejectedWith('NoPermission'); --- a/tests/src/eth/collectionSponsoring.test.ts +++ b/tests/src/eth/collectionSponsoring.test.ts @@ -1,5 +1,5 @@ import {addToAllowListExpectSuccess, bigIntToSub, confirmSponsorshipExpectSuccess, createCollectionExpectSuccess, enablePublicMintingExpectSuccess, getDetailedCollectionInfo, setCollectionSponsorExpectSuccess} from '../util/helpers'; -import {itWeb3, createEthAccount, collectionIdToAddress, GAS_ARGS, normalizeEvents, createEthAccountWithBalance, evmCollectionHelpers, getCollectionAddressFromResult, evmCollection, ethBalanceViaSub, subToEth} 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'; --- a/tests/src/eth/contractSponsoring.test.ts +++ b/tests/src/eth/contractSponsoring.test.ts @@ -15,6 +15,7 @@ // along with Unique Network. If not, see . import {expect} from 'chai'; +import { expectSubstrateEventsAtBlock } from '../util/helpers'; import { contractHelpers, createEthAccountWithBalance, @@ -24,6 +25,7 @@ SponsoringMode, createEthAccount, ethBalanceViaSub, + normalizeEvents, } from './util/helpers'; describe('Sponsoring EVM contracts', () => { @@ -36,6 +38,41 @@ expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; }); + itWeb3('Set self sponsored events', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const flipper = await deployFlipper(web3, owner); + const helpers = contractHelpers(web3, owner); + + const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send(); + // console.log(result); + const ethEvents = normalizeEvents(result.events); + expect(ethEvents).to.be.deep.equal([ + { + address: flipper.options.address, + event: 'ContractSponsorSet', + args: { + contractAddress: flipper.options.address, + sponsor: flipper.options.address, + }, + }, + { + address: flipper.options.address, + event: 'ContractSponsorshipConfirmed', + args: { + contractAddress: flipper.options.address, + sponsor: flipper.options.address, + }, + }, + ]); + + await expectSubstrateEventsAtBlock( + api, + result.blockNumber, + 'evmContractHelpers', + ['ContractSponsorSet','ContractSponsorshipConfirmed'], + ); + }); + 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); @@ -75,6 +112,33 @@ expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true; }); + itWeb3('Set sponsor event', 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); + + const result = await helpers.methods.setSponsor(flipper.options.address, sponsor).send(); + const events = normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address: flipper.options.address, + event: 'ContractSponsorSet', + args: { + contractAddress: flipper.options.address, + sponsor: sponsor, + }, + }, + ]); + + await expectSubstrateEventsAtBlock( + api, + result.blockNumber, + 'evmContractHelpers', + ['ContractSponsorSet'], + ); + }); + 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); @@ -97,6 +161,33 @@ expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; }); + itWeb3('Confirm sponsorship event', 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 expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected; + const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor}); + const events = normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address: flipper.options.address, + event: 'ContractSponsorshipConfirmed', + args: { + contractAddress: flipper.options.address, + sponsor: sponsor, + }, + }, + ]); + + await expectSubstrateEventsAtBlock( + api, + result.blockNumber, + 'evmContractHelpers', + ['ContractSponsorshipConfirmed'], + ); + }); + 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); @@ -160,6 +251,35 @@ expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false; }); + itWeb3('Remove sponsor event', 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.removeSponsor(flipper.options.address).send(); + const events = normalizeEvents(result.events); + expect(events).to.be.deep.equal([ + { + address: flipper.options.address, + event: 'ContractSponsorRemoved', + args: { + contractAddress: flipper.options.address, + }, + }, + ]); + + await expectSubstrateEventsAtBlock( + api, + result.blockNumber, + 'evmContractHelpers', + ['ContractSponsorRemoved'], + ); + }); + 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); --- a/tests/src/eth/createNFTCollection.test.ts +++ b/tests/src/eth/createNFTCollection.test.ts @@ -181,7 +181,7 @@ }); itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => { - const owner = await createEthAccount(web3); + const owner = createEthAccount(web3); const helper = evmCollectionHelpers(web3, owner); const collectionName = 'A'; const description = 'A'; @@ -194,7 +194,7 @@ itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const notOwner = await createEthAccount(web3); + const notOwner = createEthAccount(web3); const collectionHelpers = evmCollectionHelpers(web3, owner); const result = await collectionHelpers.methods.createNonfungibleCollection('A', 'A', 'A').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); --- a/tests/src/eth/createRFTCollection.test.ts +++ b/tests/src/eth/createRFTCollection.test.ts @@ -189,7 +189,7 @@ }); itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => { - const owner = await createEthAccount(web3); + const owner = createEthAccount(web3); const helper = evmCollectionHelpers(web3, owner); const collectionName = 'A'; const description = 'A'; @@ -202,7 +202,7 @@ itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const notOwner = await createEthAccount(web3); + const notOwner = createEthAccount(web3); const collectionHelpers = evmCollectionHelpers(web3, owner); const result = await collectionHelpers.methods.createRefungibleCollection('A', 'A', 'A').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); --- a/tests/src/eth/nesting/nest.test.ts +++ b/tests/src/eth/nesting/nest.test.ts @@ -1,217 +1,220 @@ -import {ApiPromise} from '@polkadot/api'; +import {IKeyringPair} from '@polkadot/types/types'; import {Contract} from 'web3-eth-contract'; -import {expect} from 'chai'; -import Web3 from 'web3'; -import {createEthAccountWithBalance, evmCollectionHelpers, GAS_ARGS, getCollectionAddressFromResult, itWeb3, tokenIdToAddress} from '../../eth/util/helpers'; -import nonFungibleAbi from '../nonFungibleAbi.json'; +import {itEth, EthUniqueHelper, usingEthPlaygrounds, expect} from '../util/playgrounds'; + const createNestingCollection = async ( - api: ApiPromise, - web3: Web3, + helper: EthUniqueHelper, owner: string, ): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => { - const collectionHelper = evmCollectionHelpers(web3, owner); - - const result = await collectionHelper.methods - .createNonfungibleCollection('A', 'B', 'C') - .send(); - const {collectionIdAddress: collectionAddress, collectionId} = await getCollectionAddressFromResult(api, result); + const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C'); - const contract = new web3.eth.Contract(nonFungibleAbi as any, collectionAddress, {from: owner, ...GAS_ARGS}); + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); await contract.methods.setCollectionNesting(true).send({from: owner}); return {collectionId, collectionAddress, contract}; }; -describe('Integration Test: EVM Nesting', () => { - itWeb3('NFT: allows an Owner to nest/unnest their token', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const {collectionId, contract} = await createNestingCollection(api, web3, owner); - // Create a token to be nested - const targetNFTTokenId = await contract.methods.nextTokenId().call(); - await contract.methods.mint( - owner, - targetNFTTokenId, - ).send({from: owner}); - - const targetNftTokenAddress = tokenIdToAddress(collectionId, targetNFTTokenId); - - // Create a nested token - const firstTokenId = await contract.methods.nextTokenId().call(); - await contract.methods.mint( - targetNftTokenAddress, - firstTokenId, - ).send({from: owner}); +describe('EVM nesting tests group', () => { + let donor: IKeyringPair; - expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress); - - // Create a token to be nested and nest - const secondTokenId = await contract.methods.nextTokenId().call(); - await contract.methods.mint( - owner, - secondTokenId, - ).send({from: owner}); - - await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner}); - - expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress); - - // Unnest token back - await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner}); - expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner); + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = privateKey('//Alice'); + }); }); - itWeb3('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - - const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(api, web3, owner); - const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(api, web3, owner); - await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner}); - - // Create a token to nest into - const targetNftTokenId = await contractA.methods.nextTokenId().call(); - await contractA.methods.mint( - owner, - targetNftTokenId, - ).send({from: owner}); - const nftTokenAddressA1 = tokenIdToAddress(collectionIdA, targetNftTokenId); - - // Create a token for nesting in the same collection as the target - const nftTokenIdA = await contractA.methods.nextTokenId().call(); - await contractA.methods.mint( - owner, - nftTokenIdA, - ).send({from: owner}); - - // Create a token for nesting in a different collection - const nftTokenIdB = await contractB.methods.nextTokenId().call(); - await contractB.methods.mint( - owner, - nftTokenIdB, - ).send({from: owner}); - - // Nest - await contractA.methods.transfer(nftTokenAddressA1, nftTokenIdA).send({from: owner}); - expect(await contractA.methods.ownerOf(nftTokenIdA).call()).to.be.equal(nftTokenAddressA1); - - await contractB.methods.transfer(nftTokenAddressA1, nftTokenIdB).send({from: owner}); - expect(await contractB.methods.ownerOf(nftTokenIdB).call()).to.be.equal(nftTokenAddressA1); - }); -}); - -describe('Negative Test: EVM Nesting', async() => { - itWeb3('NFT: disallows to nest token if nesting is disabled', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - - const {collectionId, contract} = await createNestingCollection(api, web3, owner); - await contract.methods.setCollectionNesting(false).send({from: owner}); - - // Create a token to nest into - const targetNftTokenId = await contract.methods.nextTokenId().call(); - await contract.methods.mint( - owner, - targetNftTokenId, - ).send({from: owner}); - - const targetNftTokenAddress = tokenIdToAddress(collectionId, targetNftTokenId); - - // Create a token to nest - const nftTokenId = await contract.methods.nextTokenId().call(); - await contract.methods.mint( - owner, - nftTokenId, - ).send({from: owner}); - - // Try to nest - await expect(contract.methods - .transfer(targetNftTokenAddress, nftTokenId) - .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest'); - }); - - itWeb3('NFT: disallows a non-Owner to nest someone else\'s token', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const malignant = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - - const {collectionId, contract} = await createNestingCollection(api, web3, owner); - - // Mint a token - const targetTokenId = await contract.methods.nextTokenId().call(); - await contract.methods.mint( - owner, - targetTokenId, - ).send({from: owner}); - const targetTokenAddress = tokenIdToAddress(collectionId, targetTokenId); - - // Mint a token belonging to a different account - const tokenId = await contract.methods.nextTokenId().call(); - await contract.methods.mint( - malignant, - tokenId, - ).send({from: owner}); - - // Try to nest one token in another as a non-owner account - await expect(contract.methods - .transfer(targetTokenAddress, tokenId) - .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest'); - }); - - itWeb3('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const malignant = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - - const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(api, web3, owner); - const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(api, web3, owner); - - await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner}); - - // Create a token in one collection - const nftTokenIdA = await contractA.methods.nextTokenId().call(); - await contractA.methods.mint( - owner, - nftTokenIdA, - ).send({from: owner}); - const nftTokenAddressA = tokenIdToAddress(collectionIdA, nftTokenIdA); - - // Create a token in another collection belonging to someone else - const nftTokenIdB = await contractB.methods.nextTokenId().call(); - await contractB.methods.mint( - malignant, - nftTokenIdB, - ).send({from: owner}); - - // Try to drag someone else's token into the other collection and nest - await expect(contractB.methods - .transfer(nftTokenAddressA, nftTokenIdB) - .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest'); + describe('Integration Test: EVM Nesting', () => { + itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId, contract} = await createNestingCollection(helper, owner); + + // Create a token to be nested + const targetNFTTokenId = await contract.methods.nextTokenId().call(); + await contract.methods.mint( + owner, + targetNFTTokenId, + ).send({from: owner}); + + const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId); + + // Create a nested token + const firstTokenId = await contract.methods.nextTokenId().call(); + await contract.methods.mint( + targetNftTokenAddress, + firstTokenId, + ).send({from: owner}); + + expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress); + + // Create a token to be nested and nest + const secondTokenId = await contract.methods.nextTokenId().call(); + await contract.methods.mint( + owner, + secondTokenId, + ).send({from: owner}); + + await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner}); + + expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress); + + // Unnest token back + await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner}); + expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner); + }); + + itEth('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner); + const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(helper, owner); + await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner}); + + // Create a token to nest into + const targetNftTokenId = await contractA.methods.nextTokenId().call(); + await contractA.methods.mint( + owner, + targetNftTokenId, + ).send({from: owner}); + const nftTokenAddressA1 = helper.ethAddress.fromTokenId(collectionIdA, targetNftTokenId); + + // Create a token for nesting in the same collection as the target + const nftTokenIdA = await contractA.methods.nextTokenId().call(); + await contractA.methods.mint( + owner, + nftTokenIdA, + ).send({from: owner}); + + // Create a token for nesting in a different collection + const nftTokenIdB = await contractB.methods.nextTokenId().call(); + await contractB.methods.mint( + owner, + nftTokenIdB, + ).send({from: owner}); + + // Nest + await contractA.methods.transfer(nftTokenAddressA1, nftTokenIdA).send({from: owner}); + expect(await contractA.methods.ownerOf(nftTokenIdA).call()).to.be.equal(nftTokenAddressA1); + + await contractB.methods.transfer(nftTokenAddressA1, nftTokenIdB).send({from: owner}); + expect(await contractB.methods.ownerOf(nftTokenIdB).call()).to.be.equal(nftTokenAddressA1); + }); }); - itWeb3('NFT: disallows to nest token in an unlisted collection', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - - const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(api, web3, owner); - const {contract: contractB} = await createNestingCollection(api, web3, owner); - - await contractA.methods.setCollectionNesting(true, [collectionAddressA]).send({from: owner}); - - // Create a token in one collection - const nftTokenIdA = await contractA.methods.nextTokenId().call(); - await contractA.methods.mint( - owner, - nftTokenIdA, - ).send({from: owner}); - const nftTokenAddressA = tokenIdToAddress(collectionIdA, nftTokenIdA); - - // Create a token in another collection - const nftTokenIdB = await contractB.methods.nextTokenId().call(); - await contractB.methods.mint( - owner, - nftTokenIdB, - ).send({from: owner}); - - // Try to nest into a token in the other collection, disallowed in the first - await expect(contractB.methods - .transfer(nftTokenAddressA, nftTokenIdB) - .call()).to.be.rejectedWith('SourceCollectionIsNotAllowedToNest'); + describe('Negative Test: EVM Nesting', async() => { + itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionId, contract} = await createNestingCollection(helper, owner); + await contract.methods.setCollectionNesting(false).send({from: owner}); + + // Create a token to nest into + const targetNftTokenId = await contract.methods.nextTokenId().call(); + await contract.methods.mint( + owner, + targetNftTokenId, + ).send({from: owner}); + + const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNftTokenId); + + // Create a token to nest + const nftTokenId = await contract.methods.nextTokenId().call(); + await contract.methods.mint( + owner, + nftTokenId, + ).send({from: owner}); + + // Try to nest + await expect(contract.methods + .transfer(targetNftTokenAddress, nftTokenId) + .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest'); + }); + + itEth('NFT: disallows a non-Owner to nest someone else\'s token', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const malignant = await helper.eth.createAccountWithBalance(donor); + + const {collectionId, contract} = await createNestingCollection(helper, owner); + + // Mint a token + const targetTokenId = await contract.methods.nextTokenId().call(); + await contract.methods.mint( + owner, + targetTokenId, + ).send({from: owner}); + const targetTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId); + + // Mint a token belonging to a different account + const tokenId = await contract.methods.nextTokenId().call(); + await contract.methods.mint( + malignant, + tokenId, + ).send({from: owner}); + + // Try to nest one token in another as a non-owner account + await expect(contract.methods + .transfer(targetTokenAddress, tokenId) + .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest'); + }); + + itEth('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const malignant = await helper.eth.createAccountWithBalance(donor); + + const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner); + const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(helper, owner); + + await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner}); + + // Create a token in one collection + const nftTokenIdA = await contractA.methods.nextTokenId().call(); + await contractA.methods.mint( + owner, + nftTokenIdA, + ).send({from: owner}); + const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA); + + // Create a token in another collection belonging to someone else + const nftTokenIdB = await contractB.methods.nextTokenId().call(); + await contractB.methods.mint( + malignant, + nftTokenIdB, + ).send({from: owner}); + + // Try to drag someone else's token into the other collection and nest + await expect(contractB.methods + .transfer(nftTokenAddressA, nftTokenIdB) + .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest'); + }); + + itEth('NFT: disallows to nest token in an unlisted collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner); + const {contract: contractB} = await createNestingCollection(helper, owner); + + await contractA.methods.setCollectionNesting(true, [collectionAddressA]).send({from: owner}); + + // Create a token in one collection + const nftTokenIdA = await contractA.methods.nextTokenId().call(); + await contractA.methods.mint( + owner, + nftTokenIdA, + ).send({from: owner}); + const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA); + + // Create a token in another collection + const nftTokenIdB = await contractB.methods.nextTokenId().call(); + await contractB.methods.mint( + owner, + nftTokenIdB, + ).send({from: owner}); + + // Try to nest into a token in the other collection, disallowed in the first + await expect(contractB.methods + .transfer(nftTokenAddressA, nftTokenIdB) + .call()).to.be.rejectedWith('SourceCollectionIsNotAllowedToNest'); + }); }); }); --- a/tests/src/eth/payable.test.ts +++ b/tests/src/eth/payable.test.ts @@ -14,97 +14,91 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import {expect} from 'chai'; -import {submitTransactionAsync} from '../substrate/substrate-api'; -import {createEthAccountWithBalance, deployCollector, GAS_ARGS, itWeb3, subToEth, transferBalanceToEth} from './util/helpers'; -import {evmToAddress} from '@polkadot/util-crypto'; -import {getGenericResult, UNIQUE} from '../util/helpers'; -import {getBalanceSingle, transferBalanceExpectSuccess} from '../substrate/get-balance'; +import {IKeyringPair} from '@polkadot/types/types'; + +import {itEth, expect, usingEthPlaygrounds} from './util/playgrounds'; describe('EVM payable contracts', () => { - itWeb3('Evm contract can receive wei from eth account', async ({api, web3, privateKeyWrapper}) => { - const deployer = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const contract = await deployCollector(web3, deployer); + let donor: IKeyringPair; - await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', ...GAS_ARGS}); + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = privateKey('//Alice'); + }); + }); + + itEth('Evm contract can receive wei from eth account', async ({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const contract = await helper.eth.deployCollectorContract(deployer); + + const web3 = helper.getWeb3(); + + await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', gas: helper.eth.DEFAULT_GAS}); expect(await contract.methods.getCollected().call()).to.be.equal('10000'); }); - itWeb3('Evm contract can receive wei from substrate account', async ({api, web3, privateKeyWrapper}) => { - const deployer = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const contract = await deployCollector(web3, deployer); - const alice = privateKeyWrapper('//Alice'); + itEth('Evm contract can receive wei from substrate account', async ({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const contract = await helper.eth.deployCollectorContract(deployer); + const [alice] = await helper.arrange.createAccounts([10n], donor); + + const weiCount = '10000'; // Transaction fee/value will be payed from subToEth(sender) evm balance, // which is backed by evmToAddress(subToEth(sender)) substrate balance - await transferBalanceToEth(api, alice, subToEth(alice.address)); + await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address), 5n); + - { - const tx = api.tx.evm.call( - subToEth(alice.address), - contract.options.address, - contract.methods.giveMoney().encodeABI(), - '10000', - GAS_ARGS.gas, - await web3.eth.getGasPrice(), - null, - null, - [], - ); - const events = await submitTransactionAsync(alice, tx); - const result = getGenericResult(events); - expect(result.success).to.be.true; - } + await helper.eth.callEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount); - expect(await contract.methods.getCollected().call()).to.be.equal('10000'); + expect(await contract.methods.getCollected().call()).to.be.equal(weiCount); }); // We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible - itWeb3('Wei sent directly to backing storage of evm contract balance is unaccounted', async({api, web3, privateKeyWrapper}) => { - const deployer = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const contract = await deployCollector(web3, deployer); - const alice = privateKeyWrapper('//Alice'); + itEth('Wei sent directly to backing storage of evm contract balance is unaccounted', async({helper}) => { + const deployer = await helper.eth.createAccountWithBalance(donor); + const contract = await helper.eth.deployCollectorContract(deployer); + const [alice] = await helper.arrange.createAccounts([10n], donor); + + const weiCount = 10_000n; - await transferBalanceExpectSuccess(api, alice, evmToAddress(contract.options.address), '10000'); + await helper.eth.transferBalanceFromSubstrate(alice, contract.options.address, weiCount, false); - expect(await contract.methods.getUnaccounted().call()).to.be.equal('10000'); + expect(await contract.methods.getUnaccounted().call()).to.be.equal(weiCount.toString()); }); - itWeb3('Balance can be retrieved from evm contract', async({api, web3, privateKeyWrapper}) => { - const FEE_BALANCE = 1000n * UNIQUE; - const CONTRACT_BALANCE = 1n * UNIQUE; + itEth('Balance can be retrieved from evm contract', async({helper, privateKey}) => { + const FEE_BALANCE = 10n * helper.balance.getOneTokenNominal(); + const CONTRACT_BALANCE = 1n * helper.balance.getOneTokenNominal(); + + const deployer = await helper.eth.createAccountWithBalance(donor); + const contract = await helper.eth.deployCollectorContract(deployer); + const [alice] = await helper.arrange.createAccounts([20n], donor); - const deployer = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const contract = await deployCollector(web3, deployer); - const alice = privateKeyWrapper('//Alice'); + const web3 = helper.getWeb3(); - await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), ...GAS_ARGS}); + await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS}); - const receiver = privateKeyWrapper(`//Receiver${Date.now()}`); + const receiver = privateKey(`//Receiver${Date.now()}`); // First receive balance on eth balance of bob { - const ethReceiver = subToEth(receiver.address); + const ethReceiver = helper.address.substrateToEth(receiver.address); expect(await web3.eth.getBalance(ethReceiver)).to.be.equal('0'); await contract.methods.withdraw(ethReceiver).send({from: deployer}); expect(await web3.eth.getBalance(ethReceiver)).to.be.equal(CONTRACT_BALANCE.toString()); } // Some balance is required to pay fee for evm.withdraw call - await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString()); + await helper.balance.transferToSubstrate(alice, receiver.address, FEE_BALANCE); + // await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString()); // Withdraw balance from eth to substrate { - const initialReceiverBalance = await getBalanceSingle(api, receiver.address); - const tx = api.tx.evm.withdraw( - subToEth(receiver.address), - CONTRACT_BALANCE.toString(), - ); - const events = await submitTransactionAsync(receiver, tx); - const result = getGenericResult(events); - expect(result.success).to.be.true; - const finalReceiverBalance = await getBalanceSingle(api, receiver.address); + const initialReceiverBalance = await helper.balance.getSubstrate(receiver.address); + await helper.executeExtrinsic(receiver, 'api.tx.evm.withdraw', [helper.address.substrateToEth(receiver.address), CONTRACT_BALANCE.toString()], true); + const finalReceiverBalance = await helper.balance.getSubstrate(receiver.address); expect(finalReceiverBalance > initialReceiverBalance).to.be.true; } --- a/tests/src/eth/reFungibleToken.test.ts +++ b/tests/src/eth/reFungibleToken.test.ts @@ -15,7 +15,7 @@ // along with Unique Network. If not, see . import {approve, createCollection, createRefungibleToken, transfer, transferFrom, UNIQUE, requirePallets, Pallets} from '../util/helpers'; -import {collectionIdFromAddress, collectionIdToAddress, createEthAccount, createEthAccountWithBalance, createNonfungibleCollection, createRefungibleCollection, evmCollection, evmCollectionHelpers, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, tokenIdToAddress, transferBalanceToEth, uniqueNFT, uniqueRefungible, uniqueRefungibleToken} from './util/helpers'; +import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, createRefungibleCollection, evmCollection, evmCollectionHelpers, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, tokenIdToAddress, transferBalanceToEth, uniqueRefungible, uniqueRefungibleToken} from './util/helpers'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; --- 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", --- /dev/null +++ b/tests/src/eth/util/playgrounds/index.ts @@ -0,0 +1,49 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +import {IKeyringPair} from '@polkadot/types/types'; + +import config from '../../../config'; + +import {EthUniqueHelper} from './unique.dev'; +import {SilentLogger, SilentConsole} from '../../../util/playgrounds/unique.dev'; + +export {EthUniqueHelper} from './unique.dev'; + +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +chai.use(chaiAsPromised); +export const expect = chai.expect; + +export const usingEthPlaygrounds = async (code: (helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise) => { + const silentConsole = new SilentConsole(); + silentConsole.enable(); + + const helper = new EthUniqueHelper(new SilentLogger()); + + try { + await helper.connect(config.substrateUrl); + await helper.connectWeb3(config.substrateUrl); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const privateKey = (seed: string) => helper.util.fromSeed(seed, ss58Format); + await code(helper, privateKey); + } + finally { + await helper.disconnect(); + await helper.disconnectWeb3(); + silentConsole.disable(); + } +}; + +export async function itEth(name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any, opts: { only?: boolean, skip?: boolean } = {}) { + let i: any = it; + if (opts.only) i = i.only; + else if (opts.skip) i = i.skip; + i(name, async () => { + await usingEthPlaygrounds(async (helper, privateKey) => { + await cb({helper, privateKey}); + }); + }); +} +itEth.only = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itEth(name, cb, {only: true}); +itEth.skip = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itEth(name, cb, {skip: true}); \ No newline at end of file --- /dev/null +++ b/tests/src/eth/util/playgrounds/types.ts @@ -0,0 +1,9 @@ +export interface ContractImports { + solPath: string; + fsPath: string; +} + +export interface CompiledContract { + abi: any; + object: string; +} \ No newline at end of file --- /dev/null +++ b/tests/src/eth/util/playgrounds/unique.dev.ts @@ -0,0 +1,268 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +/* eslint-disable function-call-argument-newline */ + +import {readFile} from 'fs/promises'; + +import Web3 from 'web3'; +import {WebsocketProvider} from 'web3-core'; +import {Contract} from 'web3-eth-contract'; + +import * as solc from 'solc'; + +import {evmToAddress} from '@polkadot/util-crypto'; +import {IKeyringPair} from '@polkadot/types/types'; + +import {DevUniqueHelper} from '../../../util/playgrounds/unique.dev'; + +import {ContractImports, CompiledContract} from './types'; + +// Native contracts ABI +import collectionHelpersAbi from '../../collectionHelpersAbi.json'; +import fungibleAbi from '../../fungibleAbi.json'; +import nonFungibleAbi from '../../nonFungibleAbi.json'; +import refungibleAbi from '../../reFungibleAbi.json'; +import refungibleTokenAbi from '../../reFungibleTokenAbi.json'; +import contractHelpersAbi from './../contractHelpersAbi.json'; + +class EthGroupBase { + helper: EthUniqueHelper; + + constructor(helper: EthUniqueHelper) { + this.helper = helper; + } +} + + +class ContractGroup extends EthGroupBase { + async findImports(imports?: ContractImports[]){ + if(!imports) return function(path: string) { + return {error: `File not found: ${path}`}; + }; + + const knownImports = {} as any; + for(const imp of imports) { + knownImports[imp.solPath] = (await readFile(imp.fsPath)).toString(); + } + + return function(path: string) { + if(knownImports.hasOwnPropertyDescriptor(path)) return {contents: knownImports[path]}; + return {error: `File not found: ${path}`}; + }; + } + + async compile(name: string, src: string, imports?: ContractImports[]): Promise { + const out = JSON.parse(solc.compile(JSON.stringify({ + language: 'Solidity', + sources: { + [`${name}.sol`]: { + content: src, + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['*'], + }, + }, + }, + }), {import: await this.findImports(imports)})).contracts[`${name}.sol`][name]; + + return { + abi: out.abi, + object: '0x' + out.evm.bytecode.object, + }; + } + + async deployByCode(signer: string, name: string, src: string, imports?: ContractImports[]): Promise { + const compiledContract = await this.compile(name, src, imports); + return this.deployByAbi(signer, compiledContract.abi, compiledContract.object); + } + + async deployByAbi(signer: string, abi: any, object: string): Promise { + const web3 = this.helper.getWeb3(); + const contract = new web3.eth.Contract(abi, undefined, { + data: object, + from: signer, + gas: this.helper.eth.DEFAULT_GAS, + }); + return await contract.deploy({data: object}).send({from: signer}); + } + +} + +class NativeContractGroup extends EthGroupBase { + + contractHelpers(caller: string): Contract { + const web3 = this.helper.getWeb3(); + return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, gas: this.helper.eth.DEFAULT_GAS}); + } + + collectionHelpers(caller: string) { + const web3 = this.helper.getWeb3(); + return new web3.eth.Contract(collectionHelpersAbi as any, '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f', {from: caller, gas: this.helper.eth.DEFAULT_GAS}); + } + + collection(address: string, mode: 'nft' | 'rft' | 'ft', caller?: string): Contract { + const abi = { + 'nft': nonFungibleAbi, + 'rft': refungibleAbi, + 'ft': fungibleAbi, + }[mode]; + const web3 = this.helper.getWeb3(); + return new web3.eth.Contract(abi as any, address, {gas: this.helper.eth.DEFAULT_GAS, ...(caller ? {from: caller} : {})}); + } + + rftTokenByAddress(address: string, caller?: string): Contract { + const web3 = this.helper.getWeb3(); + return new web3.eth.Contract(refungibleTokenAbi as any, address, {gas: this.helper.eth.DEFAULT_GAS, ...(caller ? {from: caller} : {})}); + } + + rftToken(collectionId: number, tokenId: number, caller?: string): Contract { + return this.rftTokenByAddress(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller); + } +} + + +class EthGroup extends EthGroupBase { + DEFAULT_GAS = 2_500_000; + + createAccount() { + const web3 = this.helper.getWeb3(); + const account = web3.eth.accounts.create(); + web3.eth.accounts.wallet.add(account.privateKey); + return account.address; + } + + async createAccountWithBalance(donor: IKeyringPair, amount=1000n) { + const account = this.createAccount(); + await this.transferBalanceFromSubstrate(donor, account, amount); + + return account; + } + + async transferBalanceFromSubstrate(donor: IKeyringPair, recepient: string, amount=1000n, inTokens=true) { + return await this.helper.balance.transferToSubstrate(donor, evmToAddress(recepient), amount * (inTokens ? this.helper.balance.getOneTokenNominal() : 1n)); + } + + async callEVM(signer: IKeyringPair, contractAddress: string, abi: any, value: string, gasLimit?: number) { + if(!gasLimit) gasLimit = this.DEFAULT_GAS; + const web3 = this.helper.getWeb3(); + const gasPrice = await web3.eth.getGasPrice(); + // TODO: check execution status + await this.helper.executeExtrinsic( + signer, + 'api.tx.evm.call', [this.helper.address.substrateToEth(signer.address), contractAddress, abi, value, gasLimit, gasPrice, null, null, []], + true, + ); + } + + async createNonfungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> { + const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer); + + const result = await collectionHelper.methods.createNonfungibleCollection(name, description, tokenPrefix).send(); + + const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); + const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress); + + return {collectionId, collectionAddress}; + } + + async deployCollectorContract(signer: string): Promise { + return await this.helper.ethContract.deployByCode(signer, 'Collector', ` + // SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.6; + + contract Collector { + uint256 collected; + fallback() external payable { + giveMoney(); + } + function giveMoney() public payable { + collected += msg.value; + } + function getCollected() public view returns (uint256) { + return collected; + } + function getUnaccounted() public view returns (uint256) { + return address(this).balance - collected; + } + + function withdraw(address payable target) public { + target.transfer(collected); + collected = 0; + } + } + `); + } +} + + +class EthAddressGroup extends EthGroupBase { + extractCollectionId(address: string): number { + if (!(address.length === 42 || address.length === 40)) throw new Error('address wrong format'); + return parseInt(address.substr(address.length - 8), 16); + } + + fromCollectionId(collectionId: number): string { + if (collectionId >= 0xffffffff || collectionId < 0) throw new Error('collectionId overflow'); + return Web3.utils.toChecksumAddress(`0x17c4e6453cc49aaaaeaca894e6d9683e${collectionId.toString(16).padStart(8, '0')}`); + } + + extractTokenId(address: string): {collectionId: number, tokenId: number} { + if (!address.startsWith('0x')) + throw 'address not starts with "0x"'; + if (address.length > 42) + throw 'address length is more than 20 bytes'; + return { + collectionId: Number('0x' + address.substring(address.length - 16, address.length - 8)), + tokenId: Number('0x' + address.substring(address.length - 8)), + }; + } + + fromTokenId(collectionId: number, tokenId: number): string { + return this.helper.util.getNestingTokenAddress(collectionId, tokenId); + } + + normalizeAddress(address: string): string { + return '0x' + address.substring(address.length - 40); + } +} + + +export class EthUniqueHelper extends DevUniqueHelper { + web3: Web3 | null = null; + web3Provider: WebsocketProvider | null = null; + + eth: EthGroup; + ethAddress: EthAddressGroup; + ethNativeContract: NativeContractGroup; + ethContract: ContractGroup; + + constructor(logger: { log: (msg: any, level: any) => void, level: any }) { + super(logger); + this.eth = new EthGroup(this); + this.ethAddress = new EthAddressGroup(this); + this.ethNativeContract = new NativeContractGroup(this); + this.ethContract = new ContractGroup(this); + } + + getWeb3(): Web3 { + if(this.web3 === null) throw Error('Web3 not connected'); + return this.web3; + } + + async connectWeb3(wsEndpoint: string) { + if(this.web3 !== null) return; + this.web3Provider = new Web3.providers.WebsocketProvider(wsEndpoint); + this.web3 = new Web3(this.web3Provider); + } + + async disconnectWeb3() { + if(this.web3 === null) return; + this.web3Provider?.connection.close(); + this.web3 = null; + } +} + \ No newline at end of file --- /dev/null +++ b/tests/src/interfaces/appPromotion/definitions.ts @@ -0,0 +1,58 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +type RpcParam = { + name: string; + type: string; + isOptional?: true; +}; + +const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr'; + +const crossAccountParam = (name = 'account') => ({name, type: CROSS_ACCOUNT_ID_TYPE}); +const atParam = {name: 'at', type: 'Hash', isOptional: true}; + +const fun = (description: string, params: RpcParam[], type: string) => ({ + description, + params: [...params, atParam], + type, +}); + +export default { + types: {}, + rpc: { + totalStaked: fun( + 'Returns the total amount of staked tokens', + [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}], + 'u128', + ), + totalStakedPerBlock: fun( + 'Returns the total amount of staked tokens per block when staked', + [crossAccountParam('staker')], + 'Vec<(u32, u128)>', + ), + pendingUnstake: fun( + 'Returns the total amount of unstaked tokens', + [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}], + 'u128', + ), + pendingUnstakePerBlock: fun( + 'Returns the total amount of unstaked tokens per block', + [crossAccountParam('staker')], + 'Vec<(u32, u128)>', + ), + }, +}; --- /dev/null +++ b/tests/src/interfaces/appPromotion/index.ts @@ -0,0 +1,4 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export * from './types'; --- /dev/null +++ b/tests/src/interfaces/appPromotion/types.ts @@ -0,0 +1,4 @@ +// Auto-generated via `yarn polkadot-types-from-defs`, do not edit +/* eslint-disable */ + +export type PHANTOM_APPPROMOTION = 'appPromotion'; --- a/tests/src/interfaces/augment-api-consts.ts +++ b/tests/src/interfaces/augment-api-consts.ts @@ -8,13 +8,39 @@ import type { ApiTypes, AugmentedConst } from '@polkadot/api-base/types'; import type { Option, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; import type { Codec } from '@polkadot/types-codec/types'; -import type { Permill } from '@polkadot/types/interfaces/runtime'; +import type { Perbill, Permill } from '@polkadot/types/interfaces/runtime'; import type { FrameSupportPalletId, FrameSupportWeightsRuntimeDbWeight, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, SpVersionRuntimeVersion } from '@polkadot/types/lookup'; export type __AugmentedConst = AugmentedConst; declare module '@polkadot/api-base/types/consts' { interface AugmentedConsts { + appPromotion: { + /** + * Rate of return for interval in blocks defined in `RecalculationInterval`. + **/ + intervalIncome: Perbill & AugmentedConst; + /** + * Decimals for the `Currency`. + **/ + nominal: u128 & AugmentedConst; + /** + * The app's pallet id, used for deriving its sovereign account ID. + **/ + palletId: FrameSupportPalletId & AugmentedConst; + /** + * In parachain blocks. + **/ + pendingInterval: u32 & AugmentedConst; + /** + * In relay blocks. + **/ + recalculationInterval: u32 & AugmentedConst; + /** + * Generic const + **/ + [key: string]: Codec; + }; balances: { /** * The minimum amount required to keep an account open. --- a/tests/src/interfaces/augment-api-errors.ts +++ b/tests/src/interfaces/augment-api-errors.ts @@ -11,6 +11,36 @@ declare module '@polkadot/api-base/types/errors' { interface AugmentedErrors { + appPromotion: { + /** + * Error due to action requiring admin to be set. + **/ + AdminNotSet: AugmentedError; + /** + * Errors caused by incorrect actions with a locked balance. + **/ + IncorrectLockedBalanceOperation: AugmentedError; + /** + * No permission to perform an action. + **/ + NoPermission: AugmentedError; + /** + * Insufficient funds to perform an action. + **/ + NotSufficientFunds: AugmentedError; + /** + * Occurs when a pending unstake cannot be added in this block. PENDING_LIMIT_PER_BLOCK` limits exceeded. + **/ + PendingForBlockOverflow: AugmentedError; + /** + * The error is due to the fact that the collection/contract must already be sponsored in order to perform the action. + **/ + SponsorNotSet: AugmentedError; + /** + * Generic error + **/ + [key: string]: AugmentedError; + }; balances: { /** * Beneficiary account must pre-exist @@ -265,8 +295,12 @@ }; evmContractHelpers: { /** - * This method is only executable by owner + * No pending sponsor for contract. **/ + NoPendingSponsor: AugmentedError; + /** + * This method is only executable by contract owner + **/ NoPermission: AugmentedError; /** * Generic error @@ -274,7 +308,13 @@ [key: string]: AugmentedError; }; evmMigration: { + /** + * Migration of this account is not yet started, or already finished. + **/ AccountIsNotMigrating: AugmentedError; + /** + * Can only migrate to empty address. + **/ AccountNotEmpty: AugmentedError; /** * Generic error --- a/tests/src/interfaces/augment-api-events.ts +++ b/tests/src/interfaces/augment-api-events.ts @@ -15,6 +15,44 @@ declare module '@polkadot/api-base/types/events' { interface AugmentedEvents { + appPromotion: { + /** + * The admin was set + * + * # Arguments + * * AccountId: ID of the admin + **/ + SetAdmin: AugmentedEvent; + /** + * Staking was performed + * + * # Arguments + * * AccountId: ID of the staker + * * Balance : staking amount + **/ + Stake: AugmentedEvent; + /** + * Staking recalculation was performed + * + * # Arguments + * * AccountId: ID of the staker. + * * Balance : recalculation base + * * Balance : total income + **/ + StakingRecalculation: AugmentedEvent; + /** + * Unstaking was performed + * + * # Arguments + * * AccountId: ID of the staker + * * Balance : unstaking amount + **/ + Unstake: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; balances: { /** * A balance was set by root. @@ -208,6 +246,24 @@ **/ [key: string]: AugmentedEvent; }; + evmContractHelpers: { + /** + * Collection sponsor was removed. + **/ + ContractSponsorRemoved: AugmentedEvent; + /** + * Contract sponsor was set. + **/ + ContractSponsorSet: AugmentedEvent; + /** + * New sponsor was confirm. + **/ + ContractSponsorshipConfirmed: AugmentedEvent; + /** + * Generic event + **/ + [key: string]: AugmentedEvent; + }; parachainSystem: { /** * Downward messages were processed using the given weight. --- a/tests/src/interfaces/augment-api-query.ts +++ b/tests/src/interfaces/augment-api-query.ts @@ -6,10 +6,10 @@ import '@polkadot/api-base/types/storage'; import type { ApiTypes, AugmentedQuery, QueryableStorageEntry } from '@polkadot/api-base/types'; -import type { BTreeMap, Bytes, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64 } from '@polkadot/types-codec'; +import type { BTreeMap, Bytes, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; import type { AnyNumber, ITuple } from '@polkadot/types-codec/types'; import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime'; -import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportWeightsPerDispatchClassU64, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletUniqueSchedulerScheduledV3, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsTokenChild } from '@polkadot/types/lookup'; +import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportWeightsPerDispatchClassU64, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletUniqueSchedulerScheduledV3, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild } from '@polkadot/types/lookup'; import type { Observable } from '@polkadot/types/types'; export type __AugmentedQuery = AugmentedQuery unknown>; @@ -17,6 +17,28 @@ declare module '@polkadot/api-base/types/storage' { interface AugmentedQueries { + appPromotion: { + admin: AugmentedQuery Observable>, []> & QueryableStorageEntry; + /** + * Stores a key for record for which the next revenue recalculation would be performed. + * If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers. + **/ + nextCalculatedRecord: AugmentedQuery Observable>>, []> & QueryableStorageEntry; + pendingUnstake: AugmentedQuery Observable>>, [u32]> & QueryableStorageEntry; + /** + * Amount of tokens staked by account in the blocknumber. + **/ + staked: AugmentedQuery Observable>, [AccountId32, u32]> & QueryableStorageEntry; + /** + * Amount of stakes for an Account + **/ + stakesPerAccount: AugmentedQuery Observable, [AccountId32]> & QueryableStorageEntry; + totalStaked: AugmentedQuery Observable, []> & QueryableStorageEntry; + /** + * Generic query + **/ + [key: string]: QueryableStorageEntry; + }; balances: { /** * The Balances pallet example of storing the balance of an account. @@ -194,12 +216,66 @@ [key: string]: QueryableStorageEntry; }; evmContractHelpers: { + /** + * 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. + **/ allowlist: AugmentedQuery Observable, [H160, H160]> & QueryableStorageEntry; + /** + * 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. + **/ allowlistEnabled: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; + /** + * Store owner for contract. + * + * * **Key** - contract address. + * * **Value** - owner for contract. + **/ owner: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; selfSponsoring: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; + /** + * Storage for last sponsored block. + * + * * **Key1** - contract address. + * * **Key2** - sponsored user address. + * * **Value** - last sponsored block number. + **/ sponsorBasket: AugmentedQuery Observable>, [H160, H160]> & QueryableStorageEntry; + /** + * Store for contract sponsorship state. + * + * * **Key** - contract address. + * * **Value** - sponsorship state. + **/ + sponsoring: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; + /** + * 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). + **/ sponsoringMode: AugmentedQuery Observable>, [H160]> & QueryableStorageEntry; + /** + * Storage for sponsoring rate limit in blocks. + * + * * **Key** - contract address. + * * **Value** - amount of sponsored blocks. + **/ sponsoringRateLimit: AugmentedQuery Observable, [H160]> & QueryableStorageEntry; /** * Generic query @@ -281,7 +357,7 @@ * * Currently used to store RMRK data. **/ - tokenAuxProperties: AugmentedQuery Observable>, [u32, u32, UpDataStructsPropertyScope, Bytes]> & QueryableStorageEntry; + tokenAuxProperties: AugmentedQuery Observable>, [u32, u32, UpDataStructsPropertyScope, Bytes]> & QueryableStorageEntry; /** * Used to enumerate token's children. **/ --- a/tests/src/interfaces/augment-api-rpc.ts +++ b/tests/src/interfaces/augment-api-rpc.ts @@ -9,7 +9,7 @@ import type { AugmentedRpc } from '@polkadot/rpc-core/types'; import type { Metadata, StorageKey } from '@polkadot/types'; import type { Bytes, HashMap, Json, Null, Option, Text, U256, U64, Vec, bool, f64, u128, u32, u64 } from '@polkadot/types-codec'; -import type { AnyNumber, Codec } from '@polkadot/types-codec/types'; +import type { AnyNumber, Codec, ITuple } from '@polkadot/types-codec/types'; import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author'; import type { EpochAuthorship } from '@polkadot/types/interfaces/babe'; import type { BeefySignedCommitment } from '@polkadot/types/interfaces/beefy'; @@ -35,6 +35,24 @@ declare module '@polkadot/rpc-core/types/jsonrpc' { interface RpcInterface { + appPromotion: { + /** + * Returns the total amount of unstaked tokens + **/ + pendingUnstake: AugmentedRpc<(staker?: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Returns the total amount of unstaked tokens per block + **/ + pendingUnstakePerBlock: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>>; + /** + * Returns the total amount of staked tokens + **/ + totalStaked: AugmentedRpc<(staker?: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; + /** + * Returns the total amount of staked tokens per block when staked + **/ + totalStakedPerBlock: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>>; + }; author: { /** * Returns true if the keystore has private keys for the given public key and key type. --- a/tests/src/interfaces/augment-api-tx.ts +++ b/tests/src/interfaces/augment-api-tx.ts @@ -17,6 +17,20 @@ declare module '@polkadot/api-base/types/submittable' { interface AugmentedSubmittables { + appPromotion: { + payoutStakers: AugmentedSubmittable<(stakersNumber: Option | null | Uint8Array | u8 | AnyNumber) => SubmittableExtrinsic, [Option]>; + setAdminAddress: AugmentedSubmittable<(admin: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletEvmAccountBasicCrossAccountIdRepr]>; + sponsorCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + sponsorContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; + stake: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u128]>; + stopSponsoringCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + stopSponsoringContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; + unstake: AugmentedSubmittable<() => SubmittableExtrinsic, []>; + /** + * Generic tx + **/ + [key: string]: SubmittableExtrinsicFunction; + }; balances: { /** * Exactly as `transfer`, except the origin must be root and the source account may be @@ -181,8 +195,21 @@ [key: string]: SubmittableExtrinsicFunction; }; evmMigration: { + /** + * Start contract migration, inserts contract stub at target address, + * and marks account as pending, allowing to insert storage + **/ begin: AugmentedSubmittable<(address: H160 | string | Uint8Array) => SubmittableExtrinsic, [H160]>; + /** + * Finish contract migration, allows it to be called. + * It is not possible to alter contract storage via [`Self::set_data`] + * after this call. + **/ finish: AugmentedSubmittable<(address: H160 | string | Uint8Array, code: Bytes | string | Uint8Array) => SubmittableExtrinsic, [H160, Bytes]>; + /** + * Insert items into contract storage, this method can be called + * multiple times + **/ setData: AugmentedSubmittable<(address: H160 | string | Uint8Array, data: Vec> | ([H256 | string | Uint8Array, H256 | string | Uint8Array])[]) => SubmittableExtrinsic, [H160, Vec>]>; /** * Generic tx --- a/tests/src/interfaces/augment-types.ts +++ b/tests/src/interfaces/augment-types.ts @@ -5,7 +5,7 @@ // this is required to allow for ambient/previous definitions import '@polkadot/types/types/registry'; -import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationCall, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default'; +import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationCall, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default'; import type { Data, StorageKey } from '@polkadot/types'; import type { BitVec, Bool, Bytes, F32, F64, I128, I16, I256, I32, I64, I8, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, f32, f64, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec'; import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets'; @@ -804,6 +804,9 @@ Owner: Owner; PageCounter: PageCounter; PageIndexData: PageIndexData; + PalletAppPromotionCall: PalletAppPromotionCall; + PalletAppPromotionError: PalletAppPromotionError; + PalletAppPromotionEvent: PalletAppPromotionEvent; PalletBalancesAccountData: PalletBalancesAccountData; PalletBalancesBalanceLock: PalletBalancesBalanceLock; PalletBalancesCall: PalletBalancesCall; @@ -832,6 +835,7 @@ PalletEvmCall: PalletEvmCall; PalletEvmCoderSubstrateError: PalletEvmCoderSubstrateError; PalletEvmContractHelpersError: PalletEvmContractHelpersError; + PalletEvmContractHelpersEvent: PalletEvmContractHelpersEvent; PalletEvmContractHelpersSponsoringModeT: PalletEvmContractHelpersSponsoringModeT; PalletEvmError: PalletEvmError; PalletEvmEvent: PalletEvmEvent; @@ -1295,7 +1299,8 @@ UpDataStructsPropertyScope: UpDataStructsPropertyScope; UpDataStructsRpcCollection: UpDataStructsRpcCollection; UpDataStructsSponsoringRateLimit: UpDataStructsSponsoringRateLimit; - UpDataStructsSponsorshipState: UpDataStructsSponsorshipState; + UpDataStructsSponsorshipStateAccountId32: UpDataStructsSponsorshipStateAccountId32; + UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: UpDataStructsSponsorshipStateBasicCrossAccountIdRepr; UpDataStructsTokenChild: UpDataStructsTokenChild; UpDataStructsTokenData: UpDataStructsTokenData; UpgradeGoAhead: UpgradeGoAhead; --- a/tests/src/interfaces/default/types.ts +++ b/tests/src/interfaces/default/types.ts @@ -806,6 +806,64 @@ readonly perPeriod: Compact; } +/** @name PalletAppPromotionCall */ +export interface PalletAppPromotionCall extends Enum { + readonly isSetAdminAddress: boolean; + readonly asSetAdminAddress: { + readonly admin: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isStake: boolean; + readonly asStake: { + readonly amount: u128; + } & Struct; + readonly isUnstake: boolean; + readonly isSponsorCollection: boolean; + readonly asSponsorCollection: { + readonly collectionId: u32; + } & Struct; + readonly isStopSponsoringCollection: boolean; + readonly asStopSponsoringCollection: { + readonly collectionId: u32; + } & Struct; + readonly isSponsorContract: boolean; + readonly asSponsorContract: { + readonly contractId: H160; + } & Struct; + readonly isStopSponsoringContract: boolean; + readonly asStopSponsoringContract: { + readonly contractId: H160; + } & Struct; + readonly isPayoutStakers: boolean; + readonly asPayoutStakers: { + readonly stakersNumber: Option; + } & Struct; + readonly type: 'SetAdminAddress' | 'Stake' | 'Unstake' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers'; +} + +/** @name PalletAppPromotionError */ +export interface PalletAppPromotionError extends Enum { + readonly isAdminNotSet: boolean; + readonly isNoPermission: boolean; + readonly isNotSufficientFunds: boolean; + readonly isPendingForBlockOverflow: boolean; + readonly isSponsorNotSet: boolean; + readonly isIncorrectLockedBalanceOperation: boolean; + readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'IncorrectLockedBalanceOperation'; +} + +/** @name PalletAppPromotionEvent */ +export interface PalletAppPromotionEvent extends Enum { + readonly isStakingRecalculation: boolean; + readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>; + readonly isStake: boolean; + readonly asStake: ITuple<[AccountId32, u128]>; + readonly isUnstake: boolean; + readonly asUnstake: ITuple<[AccountId32, u128]>; + readonly isSetAdmin: boolean; + readonly asSetAdmin: AccountId32; + readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin'; +} + /** @name PalletBalancesAccountData */ export interface PalletBalancesAccountData extends Struct { readonly free: u128; @@ -1127,7 +1185,19 @@ /** @name PalletEvmContractHelpersError */ export interface PalletEvmContractHelpersError extends Enum { readonly isNoPermission: boolean; - readonly type: 'NoPermission'; + readonly isNoPendingSponsor: boolean; + readonly type: 'NoPermission' | 'NoPendingSponsor'; +} + +/** @name PalletEvmContractHelpersEvent */ +export interface PalletEvmContractHelpersEvent extends Enum { + readonly isContractSponsorSet: boolean; + readonly asContractSponsorSet: ITuple<[H160, AccountId32]>; + readonly isContractSponsorshipConfirmed: boolean; + readonly asContractSponsorshipConfirmed: ITuple<[H160, AccountId32]>; + readonly isContractSponsorRemoved: boolean; + readonly asContractSponsorRemoved: H160; + readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved'; } /** @name PalletEvmContractHelpersSponsoringModeT */ @@ -2419,7 +2489,7 @@ readonly name: Vec; readonly description: Vec; readonly tokenPrefix: Bytes; - readonly sponsorship: UpDataStructsSponsorshipState; + readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; readonly limits: UpDataStructsCollectionLimits; readonly permissions: UpDataStructsCollectionPermissions; readonly externalCollection: bool; @@ -2580,8 +2650,7 @@ export interface UpDataStructsPropertyScope extends Enum { readonly isNone: boolean; readonly isRmrk: boolean; - readonly isEth: boolean; - readonly type: 'None' | 'Rmrk' | 'Eth'; + readonly type: 'None' | 'Rmrk'; } /** @name UpDataStructsRpcCollection */ @@ -2591,7 +2660,7 @@ readonly name: Vec; readonly description: Vec; readonly tokenPrefix: Bytes; - readonly sponsorship: UpDataStructsSponsorshipState; + readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; readonly limits: UpDataStructsCollectionLimits; readonly permissions: UpDataStructsCollectionPermissions; readonly tokenPropertyPermissions: Vec; @@ -2607,8 +2676,8 @@ readonly type: 'SponsoringDisabled' | 'Blocks'; } -/** @name UpDataStructsSponsorshipState */ -export interface UpDataStructsSponsorshipState extends Enum { +/** @name UpDataStructsSponsorshipStateAccountId32 */ +export interface UpDataStructsSponsorshipStateAccountId32 extends Enum { readonly isDisabled: boolean; readonly isUnconfirmed: boolean; readonly asUnconfirmed: AccountId32; @@ -2617,6 +2686,16 @@ readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; } +/** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr */ +export interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum { + readonly isDisabled: boolean; + readonly isUnconfirmed: boolean; + readonly asUnconfirmed: PalletEvmAccountBasicCrossAccountIdRepr; + readonly isConfirmed: boolean; + readonly asConfirmed: PalletEvmAccountBasicCrossAccountIdRepr; + readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; +} + /** @name UpDataStructsTokenChild */ export interface UpDataStructsTokenChild extends Struct { readonly token: u32; --- a/tests/src/interfaces/definitions.ts +++ b/tests/src/interfaces/definitions.ts @@ -15,5 +15,6 @@ // along with Unique Network. If not, see . export {default as unique} from './unique/definitions'; +export {default as appPromotion} from './appPromotion/definitions'; export {default as rmrk} from './rmrk/definitions'; export {default as default} from './default/definitions'; \ No newline at end of file --- a/tests/src/interfaces/lookup.ts +++ b/tests/src/interfaces/lookup.ts @@ -1057,7 +1057,18 @@ } }, /** - * Lookup103: pallet_evm::pallet::Event + * Lookup103: pallet_app_promotion::pallet::Event + **/ + PalletAppPromotionEvent: { + _enum: { + StakingRecalculation: '(AccountId32,u128,u128)', + Stake: '(AccountId32,u128)', + Unstake: '(AccountId32,u128)', + SetAdmin: 'AccountId32' + } + }, + /** + * Lookup104: pallet_evm::pallet::Event **/ PalletEvmEvent: { _enum: { @@ -1071,7 +1082,7 @@ } }, /** - * Lookup104: ethereum::log::Log + * Lookup105: ethereum::log::Log **/ EthereumLog: { address: 'H160', @@ -1079,7 +1090,7 @@ data: 'Bytes' }, /** - * Lookup108: pallet_ethereum::pallet::Event + * Lookup109: pallet_ethereum::pallet::Event **/ PalletEthereumEvent: { _enum: { @@ -1087,7 +1098,7 @@ } }, /** - * Lookup109: evm_core::error::ExitReason + * Lookup110: evm_core::error::ExitReason **/ EvmCoreErrorExitReason: { _enum: { @@ -1098,13 +1109,13 @@ } }, /** - * Lookup110: evm_core::error::ExitSucceed + * Lookup111: evm_core::error::ExitSucceed **/ EvmCoreErrorExitSucceed: { _enum: ['Stopped', 'Returned', 'Suicided'] }, /** - * Lookup111: evm_core::error::ExitError + * Lookup112: evm_core::error::ExitError **/ EvmCoreErrorExitError: { _enum: { @@ -1126,13 +1137,13 @@ } }, /** - * Lookup114: evm_core::error::ExitRevert + * Lookup115: evm_core::error::ExitRevert **/ EvmCoreErrorExitRevert: { _enum: ['Reverted'] }, /** - * Lookup115: evm_core::error::ExitFatal + * Lookup116: evm_core::error::ExitFatal **/ EvmCoreErrorExitFatal: { _enum: { @@ -1143,7 +1154,17 @@ } }, /** - * Lookup116: frame_system::Phase + * Lookup117: pallet_evm_contract_helpers::pallet::Event + **/ + PalletEvmContractHelpersEvent: { + _enum: { + ContractSponsorSet: '(H160,AccountId32)', + ContractSponsorshipConfirmed: '(H160,AccountId32)', + ContractSponsorRemoved: 'H160' + } + }, + /** + * Lookup118: frame_system::Phase **/ FrameSystemPhase: { _enum: { @@ -1153,14 +1174,14 @@ } }, /** - * Lookup118: frame_system::LastRuntimeUpgradeInfo + * Lookup120: frame_system::LastRuntimeUpgradeInfo **/ FrameSystemLastRuntimeUpgradeInfo: { specVersion: 'Compact', specName: 'Text' }, /** - * Lookup119: frame_system::pallet::Call + * Lookup121: frame_system::pallet::Call **/ FrameSystemCall: { _enum: { @@ -1198,7 +1219,7 @@ } }, /** - * Lookup124: frame_system::limits::BlockWeights + * Lookup126: frame_system::limits::BlockWeights **/ FrameSystemLimitsBlockWeights: { baseBlock: 'u64', @@ -1206,7 +1227,7 @@ perClass: 'FrameSupportWeightsPerDispatchClassWeightsPerClass' }, /** - * Lookup125: frame_support::weights::PerDispatchClass + * Lookup127: frame_support::weights::PerDispatchClass **/ FrameSupportWeightsPerDispatchClassWeightsPerClass: { normal: 'FrameSystemLimitsWeightsPerClass', @@ -1214,7 +1235,7 @@ mandatory: 'FrameSystemLimitsWeightsPerClass' }, /** - * Lookup126: frame_system::limits::WeightsPerClass + * Lookup128: frame_system::limits::WeightsPerClass **/ FrameSystemLimitsWeightsPerClass: { baseExtrinsic: 'u64', @@ -1223,13 +1244,13 @@ reserved: 'Option' }, /** - * Lookup128: frame_system::limits::BlockLength + * Lookup130: frame_system::limits::BlockLength **/ FrameSystemLimitsBlockLength: { max: 'FrameSupportWeightsPerDispatchClassU32' }, /** - * Lookup129: frame_support::weights::PerDispatchClass + * Lookup131: frame_support::weights::PerDispatchClass **/ FrameSupportWeightsPerDispatchClassU32: { normal: 'u32', @@ -1237,14 +1258,14 @@ mandatory: 'u32' }, /** - * Lookup130: frame_support::weights::RuntimeDbWeight + * Lookup132: frame_support::weights::RuntimeDbWeight **/ FrameSupportWeightsRuntimeDbWeight: { read: 'u64', write: 'u64' }, /** - * Lookup131: sp_version::RuntimeVersion + * Lookup133: sp_version::RuntimeVersion **/ SpVersionRuntimeVersion: { specName: 'Text', @@ -1257,13 +1278,13 @@ stateVersion: 'u8' }, /** - * Lookup136: frame_system::pallet::Error + * Lookup138: frame_system::pallet::Error **/ FrameSystemError: { _enum: ['InvalidSpecName', 'SpecVersionNeedsToIncrease', 'FailedToExtractRuntimeVersion', 'NonDefaultComposite', 'NonZeroRefCount', 'CallFiltered'] }, /** - * Lookup137: polkadot_primitives::v2::PersistedValidationData + * Lookup139: polkadot_primitives::v2::PersistedValidationData **/ PolkadotPrimitivesV2PersistedValidationData: { parentHead: 'Bytes', @@ -1272,19 +1293,19 @@ maxPovSize: 'u32' }, /** - * Lookup140: polkadot_primitives::v2::UpgradeRestriction + * Lookup142: polkadot_primitives::v2::UpgradeRestriction **/ PolkadotPrimitivesV2UpgradeRestriction: { _enum: ['Present'] }, /** - * Lookup141: sp_trie::storage_proof::StorageProof + * Lookup143: sp_trie::storage_proof::StorageProof **/ SpTrieStorageProof: { trieNodes: 'BTreeSet' }, /** - * Lookup143: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot + * Lookup145: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot **/ CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: { dmqMqcHead: 'H256', @@ -1293,7 +1314,7 @@ egressChannels: 'Vec<(u32,PolkadotPrimitivesV2AbridgedHrmpChannel)>' }, /** - * Lookup146: polkadot_primitives::v2::AbridgedHrmpChannel + * Lookup148: polkadot_primitives::v2::AbridgedHrmpChannel **/ PolkadotPrimitivesV2AbridgedHrmpChannel: { maxCapacity: 'u32', @@ -1304,7 +1325,7 @@ mqcHead: 'Option' }, /** - * Lookup147: polkadot_primitives::v2::AbridgedHostConfiguration + * Lookup149: polkadot_primitives::v2::AbridgedHostConfiguration **/ PolkadotPrimitivesV2AbridgedHostConfiguration: { maxCodeSize: 'u32', @@ -1318,14 +1339,14 @@ validationUpgradeDelay: 'u32' }, /** - * Lookup153: polkadot_core_primitives::OutboundHrmpMessage + * Lookup155: polkadot_core_primitives::OutboundHrmpMessage **/ PolkadotCorePrimitivesOutboundHrmpMessage: { recipient: 'u32', data: 'Bytes' }, /** - * Lookup154: cumulus_pallet_parachain_system::pallet::Call + * Lookup156: cumulus_pallet_parachain_system::pallet::Call **/ CumulusPalletParachainSystemCall: { _enum: { @@ -1344,7 +1365,7 @@ } }, /** - * Lookup155: cumulus_primitives_parachain_inherent::ParachainInherentData + * Lookup157: cumulus_primitives_parachain_inherent::ParachainInherentData **/ CumulusPrimitivesParachainInherentParachainInherentData: { validationData: 'PolkadotPrimitivesV2PersistedValidationData', @@ -1353,27 +1374,27 @@ horizontalMessages: 'BTreeMap>' }, /** - * Lookup157: polkadot_core_primitives::InboundDownwardMessage + * Lookup159: polkadot_core_primitives::InboundDownwardMessage **/ PolkadotCorePrimitivesInboundDownwardMessage: { sentAt: 'u32', msg: 'Bytes' }, /** - * Lookup160: polkadot_core_primitives::InboundHrmpMessage + * Lookup162: polkadot_core_primitives::InboundHrmpMessage **/ PolkadotCorePrimitivesInboundHrmpMessage: { sentAt: 'u32', data: 'Bytes' }, /** - * Lookup163: cumulus_pallet_parachain_system::pallet::Error + * Lookup165: cumulus_pallet_parachain_system::pallet::Error **/ CumulusPalletParachainSystemError: { _enum: ['OverlappingUpgrades', 'ProhibitedByPolkadot', 'TooBig', 'ValidationDataNotAvailable', 'HostConfigurationNotAvailable', 'NotScheduled', 'NothingAuthorized', 'Unauthorized'] }, /** - * Lookup165: pallet_balances::BalanceLock + * Lookup167: pallet_balances::BalanceLock **/ PalletBalancesBalanceLock: { id: '[u8;8]', @@ -1381,26 +1402,26 @@ reasons: 'PalletBalancesReasons' }, /** - * Lookup166: pallet_balances::Reasons + * Lookup168: pallet_balances::Reasons **/ PalletBalancesReasons: { _enum: ['Fee', 'Misc', 'All'] }, /** - * Lookup169: pallet_balances::ReserveData + * Lookup171: pallet_balances::ReserveData **/ PalletBalancesReserveData: { id: '[u8;16]', amount: 'u128' }, /** - * Lookup171: pallet_balances::Releases + * Lookup173: pallet_balances::Releases **/ PalletBalancesReleases: { _enum: ['V1_0_0', 'V2_0_0'] }, /** - * Lookup172: pallet_balances::pallet::Call + * Lookup174: pallet_balances::pallet::Call **/ PalletBalancesCall: { _enum: { @@ -1433,13 +1454,13 @@ } }, /** - * Lookup175: pallet_balances::pallet::Error + * Lookup177: pallet_balances::pallet::Error **/ PalletBalancesError: { _enum: ['VestingBalance', 'LiquidityRestrictions', 'InsufficientBalance', 'ExistentialDeposit', 'KeepAlive', 'ExistingVestingSchedule', 'DeadAccount', 'TooManyReserves'] }, /** - * Lookup177: pallet_timestamp::pallet::Call + * Lookup179: pallet_timestamp::pallet::Call **/ PalletTimestampCall: { _enum: { @@ -1449,13 +1470,13 @@ } }, /** - * Lookup179: pallet_transaction_payment::Releases + * Lookup181: pallet_transaction_payment::Releases **/ PalletTransactionPaymentReleases: { _enum: ['V1Ancient', 'V2'] }, /** - * Lookup180: pallet_treasury::Proposal + * Lookup182: pallet_treasury::Proposal **/ PalletTreasuryProposal: { proposer: 'AccountId32', @@ -1464,7 +1485,7 @@ bond: 'u128' }, /** - * Lookup183: pallet_treasury::pallet::Call + * Lookup185: pallet_treasury::pallet::Call **/ PalletTreasuryCall: { _enum: { @@ -1488,17 +1509,17 @@ } }, /** - * Lookup186: frame_support::PalletId + * Lookup188: frame_support::PalletId **/ FrameSupportPalletId: '[u8;8]', /** - * Lookup187: pallet_treasury::pallet::Error + * Lookup189: pallet_treasury::pallet::Error **/ PalletTreasuryError: { _enum: ['InsufficientProposersBalance', 'InvalidIndex', 'TooManyApprovals', 'InsufficientPermission', 'ProposalNotApproved'] }, /** - * Lookup188: pallet_sudo::pallet::Call + * Lookup190: pallet_sudo::pallet::Call **/ PalletSudoCall: { _enum: { @@ -1522,7 +1543,7 @@ } }, /** - * Lookup190: orml_vesting::module::Call + * Lookup192: orml_vesting::module::Call **/ OrmlVestingModuleCall: { _enum: { @@ -1541,7 +1562,7 @@ } }, /** - * Lookup192: cumulus_pallet_xcmp_queue::pallet::Call + * Lookup194: cumulus_pallet_xcmp_queue::pallet::Call **/ CumulusPalletXcmpQueueCall: { _enum: { @@ -1590,7 +1611,7 @@ } }, /** - * Lookup193: pallet_xcm::pallet::Call + * Lookup195: pallet_xcm::pallet::Call **/ PalletXcmCall: { _enum: { @@ -1644,7 +1665,7 @@ } }, /** - * Lookup194: xcm::VersionedXcm + * Lookup196: xcm::VersionedXcm **/ XcmVersionedXcm: { _enum: { @@ -1654,7 +1675,7 @@ } }, /** - * Lookup195: xcm::v0::Xcm + * Lookup197: xcm::v0::Xcm **/ XcmV0Xcm: { _enum: { @@ -1708,7 +1729,7 @@ } }, /** - * Lookup197: xcm::v0::order::Order + * Lookup199: xcm::v0::order::Order **/ XcmV0Order: { _enum: { @@ -1751,7 +1772,7 @@ } }, /** - * Lookup199: xcm::v0::Response + * Lookup201: xcm::v0::Response **/ XcmV0Response: { _enum: { @@ -1759,7 +1780,7 @@ } }, /** - * Lookup200: xcm::v1::Xcm + * Lookup202: xcm::v1::Xcm **/ XcmV1Xcm: { _enum: { @@ -1818,7 +1839,7 @@ } }, /** - * Lookup202: xcm::v1::order::Order + * Lookup204: xcm::v1::order::Order **/ XcmV1Order: { _enum: { @@ -1863,7 +1884,7 @@ } }, /** - * Lookup204: xcm::v1::Response + * Lookup206: xcm::v1::Response **/ XcmV1Response: { _enum: { @@ -1872,11 +1893,11 @@ } }, /** - * Lookup218: cumulus_pallet_xcm::pallet::Call + * Lookup220: cumulus_pallet_xcm::pallet::Call **/ CumulusPalletXcmCall: 'Null', /** - * Lookup219: cumulus_pallet_dmp_queue::pallet::Call + * Lookup221: cumulus_pallet_dmp_queue::pallet::Call **/ CumulusPalletDmpQueueCall: { _enum: { @@ -1887,7 +1908,7 @@ } }, /** - * Lookup220: pallet_inflation::pallet::Call + * Lookup222: pallet_inflation::pallet::Call **/ PalletInflationCall: { _enum: { @@ -1897,7 +1918,7 @@ } }, /** - * Lookup221: pallet_unique::Call + * Lookup223: pallet_unique::Call **/ PalletUniqueCall: { _enum: { @@ -2029,7 +2050,7 @@ } }, /** - * Lookup226: up_data_structs::CollectionMode + * Lookup228: up_data_structs::CollectionMode **/ UpDataStructsCollectionMode: { _enum: { @@ -2039,7 +2060,7 @@ } }, /** - * Lookup227: up_data_structs::CreateCollectionData + * Lookup229: up_data_structs::CreateCollectionData **/ UpDataStructsCreateCollectionData: { mode: 'UpDataStructsCollectionMode', @@ -2054,13 +2075,13 @@ properties: 'Vec' }, /** - * Lookup229: up_data_structs::AccessMode + * Lookup231: up_data_structs::AccessMode **/ UpDataStructsAccessMode: { _enum: ['Normal', 'AllowList'] }, /** - * Lookup231: up_data_structs::CollectionLimits + * Lookup233: up_data_structs::CollectionLimits **/ UpDataStructsCollectionLimits: { accountTokenOwnershipLimit: 'Option', @@ -2074,7 +2095,7 @@ transfersEnabled: 'Option' }, /** - * Lookup233: up_data_structs::SponsoringRateLimit + * Lookup235: up_data_structs::SponsoringRateLimit **/ UpDataStructsSponsoringRateLimit: { _enum: { @@ -2083,7 +2104,7 @@ } }, /** - * Lookup236: up_data_structs::CollectionPermissions + * Lookup238: up_data_structs::CollectionPermissions **/ UpDataStructsCollectionPermissions: { access: 'Option', @@ -2091,7 +2112,7 @@ nesting: 'Option' }, /** - * Lookup238: up_data_structs::NestingPermissions + * Lookup240: up_data_structs::NestingPermissions **/ UpDataStructsNestingPermissions: { tokenOwner: 'bool', @@ -2099,18 +2120,18 @@ restricted: 'Option' }, /** - * Lookup240: up_data_structs::OwnerRestrictedSet + * Lookup242: up_data_structs::OwnerRestrictedSet **/ UpDataStructsOwnerRestrictedSet: 'BTreeSet', /** - * Lookup245: up_data_structs::PropertyKeyPermission + * Lookup247: up_data_structs::PropertyKeyPermission **/ UpDataStructsPropertyKeyPermission: { key: 'Bytes', permission: 'UpDataStructsPropertyPermission' }, /** - * Lookup246: up_data_structs::PropertyPermission + * Lookup248: up_data_structs::PropertyPermission **/ UpDataStructsPropertyPermission: { mutable: 'bool', @@ -2118,14 +2139,14 @@ tokenOwner: 'bool' }, /** - * Lookup249: up_data_structs::Property + * Lookup251: up_data_structs::Property **/ UpDataStructsProperty: { key: 'Bytes', value: 'Bytes' }, /** - * Lookup252: up_data_structs::CreateItemData + * Lookup254: up_data_structs::CreateItemData **/ UpDataStructsCreateItemData: { _enum: { @@ -2135,26 +2156,26 @@ } }, /** - * Lookup253: up_data_structs::CreateNftData + * Lookup255: up_data_structs::CreateNftData **/ UpDataStructsCreateNftData: { properties: 'Vec' }, /** - * Lookup254: up_data_structs::CreateFungibleData + * Lookup256: up_data_structs::CreateFungibleData **/ UpDataStructsCreateFungibleData: { value: 'u128' }, /** - * Lookup255: up_data_structs::CreateReFungibleData + * Lookup257: up_data_structs::CreateReFungibleData **/ UpDataStructsCreateReFungibleData: { pieces: 'u128', properties: 'Vec' }, /** - * Lookup258: up_data_structs::CreateItemExData> + * Lookup260: up_data_structs::CreateItemExData> **/ UpDataStructsCreateItemExData: { _enum: { @@ -2165,14 +2186,14 @@ } }, /** - * Lookup260: up_data_structs::CreateNftExData> + * Lookup262: up_data_structs::CreateNftExData> **/ UpDataStructsCreateNftExData: { properties: 'Vec', owner: 'PalletEvmAccountBasicCrossAccountIdRepr' }, /** - * Lookup267: up_data_structs::CreateRefungibleExSingleOwner> + * Lookup269: up_data_structs::CreateRefungibleExSingleOwner> **/ UpDataStructsCreateRefungibleExSingleOwner: { user: 'PalletEvmAccountBasicCrossAccountIdRepr', @@ -2180,14 +2201,14 @@ properties: 'Vec' }, /** - * Lookup269: up_data_structs::CreateRefungibleExMultipleOwners> + * Lookup271: up_data_structs::CreateRefungibleExMultipleOwners> **/ UpDataStructsCreateRefungibleExMultipleOwners: { users: 'BTreeMap', properties: 'Vec' }, /** - * Lookup270: pallet_unique_scheduler::pallet::Call + * Lookup272: pallet_unique_scheduler::pallet::Call **/ PalletUniqueSchedulerCall: { _enum: { @@ -2211,7 +2232,7 @@ } }, /** - * Lookup272: frame_support::traits::schedule::MaybeHashed + * Lookup274: frame_support::traits::schedule::MaybeHashed **/ FrameSupportScheduleMaybeHashed: { _enum: { @@ -2220,7 +2241,7 @@ } }, /** - * Lookup273: pallet_configuration::pallet::Call + * Lookup275: pallet_configuration::pallet::Call **/ PalletConfigurationCall: { _enum: { @@ -2233,15 +2254,15 @@ } }, /** - * Lookup274: pallet_template_transaction_payment::Call + * Lookup276: pallet_template_transaction_payment::Call **/ PalletTemplateTransactionPaymentCall: 'Null', /** - * Lookup275: pallet_structure::pallet::Call + * Lookup277: pallet_structure::pallet::Call **/ PalletStructureCall: 'Null', /** - * Lookup276: pallet_rmrk_core::pallet::Call + * Lookup278: pallet_rmrk_core::pallet::Call **/ PalletRmrkCoreCall: { _enum: { @@ -2332,7 +2353,7 @@ } }, /** - * Lookup282: rmrk_traits::resource::ResourceTypes, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup284: rmrk_traits::resource::ResourceTypes, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsResourceResourceTypes: { _enum: { @@ -2342,7 +2363,7 @@ } }, /** - * Lookup284: rmrk_traits::resource::BasicResource> + * Lookup286: rmrk_traits::resource::BasicResource> **/ RmrkTraitsResourceBasicResource: { src: 'Option', @@ -2351,7 +2372,7 @@ thumb: 'Option' }, /** - * Lookup286: rmrk_traits::resource::ComposableResource, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup288: rmrk_traits::resource::ComposableResource, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsResourceComposableResource: { parts: 'Vec', @@ -2362,7 +2383,7 @@ thumb: 'Option' }, /** - * Lookup287: rmrk_traits::resource::SlotResource> + * Lookup289: rmrk_traits::resource::SlotResource> **/ RmrkTraitsResourceSlotResource: { base: 'u32', @@ -2373,7 +2394,7 @@ thumb: 'Option' }, /** - * Lookup290: pallet_rmrk_equip::pallet::Call + * Lookup292: pallet_rmrk_equip::pallet::Call **/ PalletRmrkEquipCall: { _enum: { @@ -2394,7 +2415,7 @@ } }, /** - * Lookup293: rmrk_traits::part::PartType, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup295: rmrk_traits::part::PartType, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsPartPartType: { _enum: { @@ -2403,7 +2424,7 @@ } }, /** - * Lookup295: rmrk_traits::part::FixedPart> + * Lookup297: rmrk_traits::part::FixedPart> **/ RmrkTraitsPartFixedPart: { id: 'u32', @@ -2411,7 +2432,7 @@ src: 'Bytes' }, /** - * Lookup296: rmrk_traits::part::SlotPart, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup298: rmrk_traits::part::SlotPart, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsPartSlotPart: { id: 'u32', @@ -2420,7 +2441,7 @@ z: 'u32' }, /** - * Lookup297: rmrk_traits::part::EquippableList> + * Lookup299: rmrk_traits::part::EquippableList> **/ RmrkTraitsPartEquippableList: { _enum: { @@ -2430,7 +2451,7 @@ } }, /** - * Lookup299: rmrk_traits::theme::Theme, sp_runtime::bounded::bounded_vec::BoundedVec>, S>> + * Lookup301: rmrk_traits::theme::Theme, sp_runtime::bounded::bounded_vec::BoundedVec>, S>> **/ RmrkTraitsTheme: { name: 'Bytes', @@ -2438,14 +2459,43 @@ inherit: 'bool' }, /** - * Lookup301: rmrk_traits::theme::ThemeProperty> + * Lookup303: rmrk_traits::theme::ThemeProperty> **/ RmrkTraitsThemeThemeProperty: { key: 'Bytes', value: 'Bytes' }, /** - * Lookup303: pallet_evm::pallet::Call + * Lookup305: pallet_app_promotion::pallet::Call + **/ + PalletAppPromotionCall: { + _enum: { + set_admin_address: { + admin: 'PalletEvmAccountBasicCrossAccountIdRepr', + }, + stake: { + amount: 'u128', + }, + unstake: 'Null', + sponsor_collection: { + collectionId: 'u32', + }, + stop_sponsoring_collection: { + collectionId: 'u32', + }, + sponsor_contract: { + contractId: 'H160', + }, + stop_sponsoring_contract: { + contractId: 'H160', + }, + payout_stakers: { + stakersNumber: 'Option' + } + } + }, + /** + * Lookup307: pallet_evm::pallet::Call **/ PalletEvmCall: { _enum: { @@ -2488,7 +2538,7 @@ } }, /** - * Lookup307: pallet_ethereum::pallet::Call + * Lookup311: pallet_ethereum::pallet::Call **/ PalletEthereumCall: { _enum: { @@ -2498,7 +2548,7 @@ } }, /** - * Lookup308: ethereum::transaction::TransactionV2 + * Lookup312: ethereum::transaction::TransactionV2 **/ EthereumTransactionTransactionV2: { _enum: { @@ -2508,7 +2558,7 @@ } }, /** - * Lookup309: ethereum::transaction::LegacyTransaction + * Lookup313: ethereum::transaction::LegacyTransaction **/ EthereumTransactionLegacyTransaction: { nonce: 'U256', @@ -2520,7 +2570,7 @@ signature: 'EthereumTransactionTransactionSignature' }, /** - * Lookup310: ethereum::transaction::TransactionAction + * Lookup314: ethereum::transaction::TransactionAction **/ EthereumTransactionTransactionAction: { _enum: { @@ -2529,7 +2579,7 @@ } }, /** - * Lookup311: ethereum::transaction::TransactionSignature + * Lookup315: ethereum::transaction::TransactionSignature **/ EthereumTransactionTransactionSignature: { v: 'u64', @@ -2537,7 +2587,7 @@ s: 'H256' }, /** - * Lookup313: ethereum::transaction::EIP2930Transaction + * Lookup317: ethereum::transaction::EIP2930Transaction **/ EthereumTransactionEip2930Transaction: { chainId: 'u64', @@ -2553,14 +2603,14 @@ s: 'H256' }, /** - * Lookup315: ethereum::transaction::AccessListItem + * Lookup319: ethereum::transaction::AccessListItem **/ EthereumTransactionAccessListItem: { address: 'H160', storageKeys: 'Vec' }, /** - * Lookup316: ethereum::transaction::EIP1559Transaction + * Lookup320: ethereum::transaction::EIP1559Transaction **/ EthereumTransactionEip1559Transaction: { chainId: 'u64', @@ -2577,7 +2627,7 @@ s: 'H256' }, /** - * Lookup317: pallet_evm_migration::pallet::Call + * Lookup321: pallet_evm_migration::pallet::Call **/ PalletEvmMigrationCall: { _enum: { @@ -2595,19 +2645,19 @@ } }, /** - * Lookup320: pallet_sudo::pallet::Error + * Lookup324: pallet_sudo::pallet::Error **/ PalletSudoError: { _enum: ['RequireSudo'] }, /** - * Lookup322: orml_vesting::module::Error + * Lookup326: orml_vesting::module::Error **/ OrmlVestingModuleError: { _enum: ['ZeroVestingPeriod', 'ZeroVestingPeriodCount', 'InsufficientBalanceToLock', 'TooManyVestingSchedules', 'AmountLow', 'MaxVestingSchedulesExceeded'] }, /** - * Lookup324: cumulus_pallet_xcmp_queue::InboundChannelDetails + * Lookup328: cumulus_pallet_xcmp_queue::InboundChannelDetails **/ CumulusPalletXcmpQueueInboundChannelDetails: { sender: 'u32', @@ -2615,19 +2665,19 @@ messageMetadata: 'Vec<(u32,PolkadotParachainPrimitivesXcmpMessageFormat)>' }, /** - * Lookup325: cumulus_pallet_xcmp_queue::InboundState + * Lookup329: cumulus_pallet_xcmp_queue::InboundState **/ CumulusPalletXcmpQueueInboundState: { _enum: ['Ok', 'Suspended'] }, /** - * Lookup328: polkadot_parachain::primitives::XcmpMessageFormat + * Lookup332: polkadot_parachain::primitives::XcmpMessageFormat **/ PolkadotParachainPrimitivesXcmpMessageFormat: { _enum: ['ConcatenatedVersionedXcm', 'ConcatenatedEncodedBlob', 'Signals'] }, /** - * Lookup331: cumulus_pallet_xcmp_queue::OutboundChannelDetails + * Lookup335: cumulus_pallet_xcmp_queue::OutboundChannelDetails **/ CumulusPalletXcmpQueueOutboundChannelDetails: { recipient: 'u32', @@ -2637,13 +2687,13 @@ lastIndex: 'u16' }, /** - * Lookup332: cumulus_pallet_xcmp_queue::OutboundState + * Lookup336: cumulus_pallet_xcmp_queue::OutboundState **/ CumulusPalletXcmpQueueOutboundState: { _enum: ['Ok', 'Suspended'] }, /** - * Lookup334: cumulus_pallet_xcmp_queue::QueueConfigData + * Lookup338: cumulus_pallet_xcmp_queue::QueueConfigData **/ CumulusPalletXcmpQueueQueueConfigData: { suspendThreshold: 'u32', @@ -2654,29 +2704,29 @@ xcmpMaxIndividualWeight: 'u64' }, /** - * Lookup336: cumulus_pallet_xcmp_queue::pallet::Error + * Lookup340: cumulus_pallet_xcmp_queue::pallet::Error **/ CumulusPalletXcmpQueueError: { _enum: ['FailedToSend', 'BadXcmOrigin', 'BadXcm', 'BadOverweightIndex', 'WeightOverLimit'] }, /** - * Lookup337: pallet_xcm::pallet::Error + * Lookup341: pallet_xcm::pallet::Error **/ PalletXcmError: { _enum: ['Unreachable', 'SendFailure', 'Filtered', 'UnweighableMessage', 'DestinationNotInvertible', 'Empty', 'CannotReanchor', 'TooManyAssets', 'InvalidOrigin', 'BadVersion', 'BadLocation', 'NoSubscription', 'AlreadySubscribed'] }, /** - * Lookup338: cumulus_pallet_xcm::pallet::Error + * Lookup342: cumulus_pallet_xcm::pallet::Error **/ CumulusPalletXcmError: 'Null', /** - * Lookup339: cumulus_pallet_dmp_queue::ConfigData + * Lookup343: cumulus_pallet_dmp_queue::ConfigData **/ CumulusPalletDmpQueueConfigData: { maxIndividual: 'u64' }, /** - * Lookup340: cumulus_pallet_dmp_queue::PageIndexData + * Lookup344: cumulus_pallet_dmp_queue::PageIndexData **/ CumulusPalletDmpQueuePageIndexData: { beginUsed: 'u32', @@ -2684,19 +2734,19 @@ overweightCount: 'u64' }, /** - * Lookup343: cumulus_pallet_dmp_queue::pallet::Error + * Lookup347: cumulus_pallet_dmp_queue::pallet::Error **/ CumulusPalletDmpQueueError: { _enum: ['Unknown', 'OverLimit'] }, /** - * Lookup347: pallet_unique::Error + * Lookup351: pallet_unique::Error **/ PalletUniqueError: { _enum: ['CollectionDecimalPointLimitExceeded', 'ConfirmUnsetSponsorFail', 'EmptyArgument', 'RepartitionCalledOnNonRefungibleCollection'] }, /** - * Lookup350: pallet_unique_scheduler::ScheduledV3, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32> + * Lookup354: pallet_unique_scheduler::ScheduledV3, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32> **/ PalletUniqueSchedulerScheduledV3: { maybeId: 'Option<[u8;16]>', @@ -2706,7 +2756,7 @@ origin: 'OpalRuntimeOriginCaller' }, /** - * Lookup351: opal_runtime::OriginCaller + * Lookup355: opal_runtime::OriginCaller **/ OpalRuntimeOriginCaller: { _enum: { @@ -2815,7 +2865,7 @@ } }, /** - * Lookup352: frame_support::dispatch::RawOrigin + * Lookup356: frame_support::dispatch::RawOrigin **/ FrameSupportDispatchRawOrigin: { _enum: { @@ -2825,7 +2875,7 @@ } }, /** - * Lookup353: pallet_xcm::pallet::Origin + * Lookup357: pallet_xcm::pallet::Origin **/ PalletXcmOrigin: { _enum: { @@ -2834,7 +2884,7 @@ } }, /** - * Lookup354: cumulus_pallet_xcm::pallet::Origin + * Lookup358: cumulus_pallet_xcm::pallet::Origin **/ CumulusPalletXcmOrigin: { _enum: { @@ -2843,7 +2893,7 @@ } }, /** - * Lookup355: pallet_ethereum::RawOrigin + * Lookup359: pallet_ethereum::RawOrigin **/ PalletEthereumRawOrigin: { _enum: { @@ -2851,17 +2901,17 @@ } }, /** - * Lookup356: sp_core::Void + * Lookup360: sp_core::Void **/ SpCoreVoid: 'Null', /** - * Lookup357: pallet_unique_scheduler::pallet::Error + * Lookup361: pallet_unique_scheduler::pallet::Error **/ PalletUniqueSchedulerError: { _enum: ['FailedToSchedule', 'NotFound', 'TargetBlockNumberInPast', 'RescheduleNoChange'] }, /** - * Lookup358: up_data_structs::Collection + * Lookup362: up_data_structs::Collection **/ UpDataStructsCollection: { owner: 'AccountId32', @@ -2869,15 +2919,15 @@ name: 'Vec', description: 'Vec', tokenPrefix: 'Bytes', - sponsorship: 'UpDataStructsSponsorshipState', + sponsorship: 'UpDataStructsSponsorshipStateAccountId32', limits: 'UpDataStructsCollectionLimits', permissions: 'UpDataStructsCollectionPermissions', externalCollection: 'bool' }, /** - * Lookup359: up_data_structs::SponsorshipState + * Lookup363: up_data_structs::SponsorshipState **/ - UpDataStructsSponsorshipState: { + UpDataStructsSponsorshipStateAccountId32: { _enum: { Disabled: 'Null', Unconfirmed: 'AccountId32', @@ -2885,7 +2935,7 @@ } }, /** - * Lookup360: up_data_structs::Properties + * Lookup364: up_data_structs::Properties **/ UpDataStructsProperties: { map: 'UpDataStructsPropertiesMapBoundedVec', @@ -2893,15 +2943,15 @@ spaceLimit: 'u32' }, /** - * Lookup361: up_data_structs::PropertiesMap> + * Lookup365: up_data_structs::PropertiesMap> **/ UpDataStructsPropertiesMapBoundedVec: 'BTreeMap', /** - * Lookup366: up_data_structs::PropertiesMap + * Lookup370: up_data_structs::PropertiesMap **/ UpDataStructsPropertiesMapPropertyPermission: 'BTreeMap', /** - * Lookup373: up_data_structs::CollectionStats + * Lookup377: up_data_structs::CollectionStats **/ UpDataStructsCollectionStats: { created: 'u32', @@ -2909,18 +2959,18 @@ alive: 'u32' }, /** - * Lookup374: up_data_structs::TokenChild + * Lookup378: up_data_structs::TokenChild **/ UpDataStructsTokenChild: { token: 'u32', collection: 'u32' }, /** - * Lookup375: PhantomType::up_data_structs + * Lookup379: PhantomType::up_data_structs **/ PhantomTypeUpDataStructs: '[(UpDataStructsTokenData,UpDataStructsRpcCollection,RmrkTraitsCollectionCollectionInfo,RmrkTraitsNftNftInfo,RmrkTraitsResourceResourceInfo,RmrkTraitsPropertyPropertyInfo,RmrkTraitsBaseBaseInfo,RmrkTraitsPartPartType,RmrkTraitsTheme,RmrkTraitsNftNftChild);0]', /** - * Lookup377: up_data_structs::TokenData> + * Lookup381: up_data_structs::TokenData> **/ UpDataStructsTokenData: { properties: 'Vec', @@ -2928,7 +2978,7 @@ pieces: 'u128' }, /** - * Lookup379: up_data_structs::RpcCollection + * Lookup383: up_data_structs::RpcCollection **/ UpDataStructsRpcCollection: { owner: 'AccountId32', @@ -2936,7 +2986,7 @@ name: 'Vec', description: 'Vec', tokenPrefix: 'Bytes', - sponsorship: 'UpDataStructsSponsorshipState', + sponsorship: 'UpDataStructsSponsorshipStateAccountId32', limits: 'UpDataStructsCollectionLimits', permissions: 'UpDataStructsCollectionPermissions', tokenPropertyPermissions: 'Vec', @@ -2944,7 +2994,7 @@ readOnly: 'bool' }, /** - * Lookup380: rmrk_traits::collection::CollectionInfo, sp_runtime::bounded::bounded_vec::BoundedVec, sp_core::crypto::AccountId32> + * Lookup384: rmrk_traits::collection::CollectionInfo, sp_runtime::bounded::bounded_vec::BoundedVec, sp_core::crypto::AccountId32> **/ RmrkTraitsCollectionCollectionInfo: { issuer: 'AccountId32', @@ -2954,7 +3004,7 @@ nftsCount: 'u32' }, /** - * Lookup381: rmrk_traits::nft::NftInfo> + * Lookup385: rmrk_traits::nft::NftInfo> **/ RmrkTraitsNftNftInfo: { owner: 'RmrkTraitsNftAccountIdOrCollectionNftTuple', @@ -2964,14 +3014,14 @@ pending: 'bool' }, /** - * Lookup383: rmrk_traits::nft::RoyaltyInfo + * Lookup387: rmrk_traits::nft::RoyaltyInfo **/ RmrkTraitsNftRoyaltyInfo: { recipient: 'AccountId32', amount: 'Permill' }, /** - * Lookup384: rmrk_traits::resource::ResourceInfo, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup388: rmrk_traits::resource::ResourceInfo, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsResourceResourceInfo: { id: 'u32', @@ -2980,14 +3030,14 @@ pendingRemoval: 'bool' }, /** - * Lookup385: rmrk_traits::property::PropertyInfo, sp_runtime::bounded::bounded_vec::BoundedVec> + * Lookup389: rmrk_traits::property::PropertyInfo, sp_runtime::bounded::bounded_vec::BoundedVec> **/ RmrkTraitsPropertyPropertyInfo: { key: 'Bytes', value: 'Bytes' }, /** - * Lookup386: rmrk_traits::base::BaseInfo> + * Lookup390: rmrk_traits::base::BaseInfo> **/ RmrkTraitsBaseBaseInfo: { issuer: 'AccountId32', @@ -2995,80 +3045,86 @@ symbol: 'Bytes' }, /** - * Lookup387: rmrk_traits::nft::NftChild + * Lookup391: rmrk_traits::nft::NftChild **/ RmrkTraitsNftNftChild: { collectionId: 'u32', nftId: 'u32' }, /** - * Lookup389: pallet_common::pallet::Error + * Lookup393: pallet_common::pallet::Error **/ PalletCommonError: { _enum: ['CollectionNotFound', 'MustBeTokenOwner', 'NoPermission', 'CantDestroyNotEmptyCollection', 'PublicMintingNotAllowed', 'AddressNotInAllowlist', 'CollectionNameLimitExceeded', 'CollectionDescriptionLimitExceeded', 'CollectionTokenPrefixLimitExceeded', 'TotalCollectionsLimitExceeded', 'CollectionAdminCountExceeded', 'CollectionLimitBoundsExceeded', 'OwnerPermissionsCantBeReverted', 'TransferNotAllowed', 'AccountTokenLimitExceeded', 'CollectionTokenLimitExceeded', 'MetadataFlagFrozen', 'TokenNotFound', 'TokenValueTooLow', 'ApprovedValueTooLow', 'CantApproveMoreThanOwned', 'AddressIsZero', 'UnsupportedOperation', 'NotSufficientFounds', 'UserIsNotAllowedToNest', 'SourceCollectionIsNotAllowedToNest', 'CollectionFieldSizeExceeded', 'NoSpaceForProperty', 'PropertyLimitReached', 'PropertyKeyIsTooLong', 'InvalidCharacterInPropertyKey', 'EmptyPropertyKey', 'CollectionIsExternal', 'CollectionIsInternal'] }, /** - * Lookup391: pallet_fungible::pallet::Error + * Lookup395: pallet_fungible::pallet::Error **/ PalletFungibleError: { _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsHaveNoId', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed'] }, /** - * Lookup392: pallet_refungible::ItemData + * Lookup396: pallet_refungible::ItemData **/ PalletRefungibleItemData: { constData: 'Bytes' }, /** - * Lookup397: pallet_refungible::pallet::Error + * Lookup401: pallet_refungible::pallet::Error **/ PalletRefungibleError: { _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RepartitionWhileNotOwningAllPieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed'] }, /** - * Lookup398: pallet_nonfungible::ItemData> + * Lookup402: pallet_nonfungible::ItemData> **/ PalletNonfungibleItemData: { owner: 'PalletEvmAccountBasicCrossAccountIdRepr' }, /** - * Lookup400: up_data_structs::PropertyScope + * Lookup404: up_data_structs::PropertyScope **/ UpDataStructsPropertyScope: { - _enum: ['None', 'Rmrk', 'Eth'] + _enum: ['None', 'Rmrk'] }, /** - * Lookup402: pallet_nonfungible::pallet::Error + * Lookup406: pallet_nonfungible::pallet::Error **/ PalletNonfungibleError: { _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount', 'CantBurnNftWithChildren'] }, /** - * Lookup403: pallet_structure::pallet::Error + * Lookup407: pallet_structure::pallet::Error **/ PalletStructureError: { _enum: ['OuroborosDetected', 'DepthLimit', 'BreadthLimit', 'TokenNotFound'] }, /** - * Lookup404: pallet_rmrk_core::pallet::Error + * Lookup408: pallet_rmrk_core::pallet::Error **/ PalletRmrkCoreError: { _enum: ['CorruptedCollectionType', 'RmrkPropertyKeyIsTooLong', 'RmrkPropertyValueIsTooLong', 'RmrkPropertyIsNotFound', 'UnableToDecodeRmrkData', 'CollectionNotEmpty', 'NoAvailableCollectionId', 'NoAvailableNftId', 'CollectionUnknown', 'NoPermission', 'NonTransferable', 'CollectionFullOrLocked', 'ResourceDoesntExist', 'CannotSendToDescendentOrSelf', 'CannotAcceptNonOwnedNft', 'CannotRejectNonOwnedNft', 'CannotRejectNonPendingNft', 'ResourceNotPending', 'NoAvailableResourceId'] }, /** - * Lookup406: pallet_rmrk_equip::pallet::Error + * Lookup410: pallet_rmrk_equip::pallet::Error **/ PalletRmrkEquipError: { _enum: ['PermissionError', 'NoAvailableBaseId', 'NoAvailablePartId', 'BaseDoesntExist', 'NeedsDefaultThemeFirst', 'PartDoesntExist', 'NoEquippableOnFixedPart'] }, /** - * Lookup409: pallet_evm::pallet::Error + * Lookup416: pallet_app_promotion::pallet::Error **/ + PalletAppPromotionError: { + _enum: ['AdminNotSet', 'NoPermission', 'NotSufficientFunds', 'PendingForBlockOverflow', 'SponsorNotSet', 'IncorrectLockedBalanceOperation'] + }, + /** + * Lookup419: pallet_evm::pallet::Error + **/ PalletEvmError: { _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce'] }, /** - * Lookup412: fp_rpc::TransactionStatus + * Lookup422: fp_rpc::TransactionStatus **/ FpRpcTransactionStatus: { transactionHash: 'H256', @@ -3080,11 +3136,11 @@ logsBloom: 'EthbloomBloom' }, /** - * Lookup414: ethbloom::Bloom + * Lookup424: ethbloom::Bloom **/ EthbloomBloom: '[u8;256]', /** - * Lookup416: ethereum::receipt::ReceiptV3 + * Lookup426: ethereum::receipt::ReceiptV3 **/ EthereumReceiptReceiptV3: { _enum: { @@ -3094,7 +3150,7 @@ } }, /** - * Lookup417: ethereum::receipt::EIP658ReceiptData + * Lookup427: ethereum::receipt::EIP658ReceiptData **/ EthereumReceiptEip658ReceiptData: { statusCode: 'u8', @@ -3103,7 +3159,7 @@ logs: 'Vec' }, /** - * Lookup418: ethereum::block::Block + * Lookup428: ethereum::block::Block **/ EthereumBlock: { header: 'EthereumHeader', @@ -3111,7 +3167,7 @@ ommers: 'Vec' }, /** - * Lookup419: ethereum::header::Header + * Lookup429: ethereum::header::Header **/ EthereumHeader: { parentHash: 'H256', @@ -3131,41 +3187,51 @@ nonce: 'EthereumTypesHashH64' }, /** - * Lookup420: ethereum_types::hash::H64 + * Lookup430: ethereum_types::hash::H64 **/ EthereumTypesHashH64: '[u8;8]', /** - * Lookup425: pallet_ethereum::pallet::Error + * Lookup435: pallet_ethereum::pallet::Error **/ PalletEthereumError: { _enum: ['InvalidSignature', 'PreLogExists'] }, /** - * Lookup426: pallet_evm_coder_substrate::pallet::Error + * Lookup436: pallet_evm_coder_substrate::pallet::Error **/ PalletEvmCoderSubstrateError: { _enum: ['OutOfGas', 'OutOfFund'] }, /** - * Lookup427: pallet_evm_contract_helpers::SponsoringModeT + * Lookup437: up_data_structs::SponsorshipState> **/ + UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: { + _enum: { + Disabled: 'Null', + Unconfirmed: 'PalletEvmAccountBasicCrossAccountIdRepr', + Confirmed: 'PalletEvmAccountBasicCrossAccountIdRepr' + } + }, + /** + * Lookup438: pallet_evm_contract_helpers::SponsoringModeT + **/ PalletEvmContractHelpersSponsoringModeT: { _enum: ['Disabled', 'Allowlisted', 'Generous'] }, /** - * Lookup429: pallet_evm_contract_helpers::pallet::Error + * Lookup440: pallet_evm_contract_helpers::pallet::Error **/ PalletEvmContractHelpersError: { - _enum: ['NoPermission'] + _enum: ['NoPermission', 'NoPendingSponsor'] }, /** - * Lookup430: pallet_evm_migration::pallet::Error + * Lookup441: pallet_evm_migration::pallet::Error **/ PalletEvmMigrationError: { _enum: ['AccountNotEmpty', 'AccountIsNotMigrating'] }, /** - * Lookup432: sp_runtime::MultiSignature + * Lookup443: sp_runtime::MultiSignature **/ SpRuntimeMultiSignature: { _enum: { @@ -3175,43 +3241,43 @@ } }, /** - * Lookup433: sp_core::ed25519::Signature + * Lookup444: sp_core::ed25519::Signature **/ SpCoreEd25519Signature: '[u8;64]', /** - * Lookup435: sp_core::sr25519::Signature + * Lookup446: sp_core::sr25519::Signature **/ SpCoreSr25519Signature: '[u8;64]', /** - * Lookup436: sp_core::ecdsa::Signature + * Lookup447: sp_core::ecdsa::Signature **/ SpCoreEcdsaSignature: '[u8;65]', /** - * Lookup439: frame_system::extensions::check_spec_version::CheckSpecVersion + * Lookup450: frame_system::extensions::check_spec_version::CheckSpecVersion **/ FrameSystemExtensionsCheckSpecVersion: 'Null', /** - * Lookup440: frame_system::extensions::check_genesis::CheckGenesis + * Lookup451: frame_system::extensions::check_genesis::CheckGenesis **/ FrameSystemExtensionsCheckGenesis: 'Null', /** - * Lookup443: frame_system::extensions::check_nonce::CheckNonce + * Lookup454: frame_system::extensions::check_nonce::CheckNonce **/ FrameSystemExtensionsCheckNonce: 'Compact', /** - * Lookup444: frame_system::extensions::check_weight::CheckWeight + * Lookup455: frame_system::extensions::check_weight::CheckWeight **/ FrameSystemExtensionsCheckWeight: 'Null', /** - * Lookup445: pallet_template_transaction_payment::ChargeTransactionPayment + * Lookup456: pallet_template_transaction_payment::ChargeTransactionPayment **/ PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact', /** - * Lookup446: opal_runtime::Runtime + * Lookup457: opal_runtime::Runtime **/ OpalRuntimeRuntime: 'Null', /** - * Lookup447: pallet_ethereum::FakeTransactionFinalizer + * Lookup458: pallet_ethereum::FakeTransactionFinalizer **/ PalletEthereumFakeTransactionFinalizer: 'Null' }; --- a/tests/src/interfaces/registry.ts +++ b/tests/src/interfaces/registry.ts @@ -5,7 +5,7 @@ // this is required to allow for ambient/previous definitions import '@polkadot/types/types/registry'; -import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationCall, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; +import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationCall, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; declare module '@polkadot/types/types/registry' { interface InterfaceTypes { @@ -83,6 +83,9 @@ OrmlVestingModuleError: OrmlVestingModuleError; OrmlVestingModuleEvent: OrmlVestingModuleEvent; OrmlVestingVestingSchedule: OrmlVestingVestingSchedule; + PalletAppPromotionCall: PalletAppPromotionCall; + PalletAppPromotionError: PalletAppPromotionError; + PalletAppPromotionEvent: PalletAppPromotionEvent; PalletBalancesAccountData: PalletBalancesAccountData; PalletBalancesBalanceLock: PalletBalancesBalanceLock; PalletBalancesCall: PalletBalancesCall; @@ -103,6 +106,7 @@ PalletEvmCall: PalletEvmCall; PalletEvmCoderSubstrateError: PalletEvmCoderSubstrateError; PalletEvmContractHelpersError: PalletEvmContractHelpersError; + PalletEvmContractHelpersEvent: PalletEvmContractHelpersEvent; PalletEvmContractHelpersSponsoringModeT: PalletEvmContractHelpersSponsoringModeT; PalletEvmError: PalletEvmError; PalletEvmEvent: PalletEvmEvent; @@ -213,7 +217,8 @@ UpDataStructsPropertyScope: UpDataStructsPropertyScope; UpDataStructsRpcCollection: UpDataStructsRpcCollection; UpDataStructsSponsoringRateLimit: UpDataStructsSponsoringRateLimit; - UpDataStructsSponsorshipState: UpDataStructsSponsorshipState; + UpDataStructsSponsorshipStateAccountId32: UpDataStructsSponsorshipStateAccountId32; + UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: UpDataStructsSponsorshipStateBasicCrossAccountIdRepr; UpDataStructsTokenChild: UpDataStructsTokenChild; UpDataStructsTokenData: UpDataStructsTokenData; XcmDoubleEncoded: XcmDoubleEncoded; --- a/tests/src/interfaces/types-lookup.ts +++ b/tests/src/interfaces/types-lookup.ts @@ -1198,7 +1198,20 @@ readonly type: 'BaseCreated' | 'EquippablesUpdated'; } - /** @name PalletEvmEvent (103) */ + /** @name PalletAppPromotionEvent (103) */ + interface PalletAppPromotionEvent extends Enum { + readonly isStakingRecalculation: boolean; + readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>; + readonly isStake: boolean; + readonly asStake: ITuple<[AccountId32, u128]>; + readonly isUnstake: boolean; + readonly asUnstake: ITuple<[AccountId32, u128]>; + readonly isSetAdmin: boolean; + readonly asSetAdmin: AccountId32; + readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin'; + } + + /** @name PalletEvmEvent (104) */ interface PalletEvmEvent extends Enum { readonly isLog: boolean; readonly asLog: EthereumLog; @@ -1217,21 +1230,21 @@ readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed' | 'BalanceDeposit' | 'BalanceWithdraw'; } - /** @name EthereumLog (104) */ + /** @name EthereumLog (105) */ interface EthereumLog extends Struct { readonly address: H160; readonly topics: Vec; readonly data: Bytes; } - /** @name PalletEthereumEvent (108) */ + /** @name PalletEthereumEvent (109) */ interface PalletEthereumEvent extends Enum { readonly isExecuted: boolean; readonly asExecuted: ITuple<[H160, H160, H256, EvmCoreErrorExitReason]>; readonly type: 'Executed'; } - /** @name EvmCoreErrorExitReason (109) */ + /** @name EvmCoreErrorExitReason (110) */ interface EvmCoreErrorExitReason extends Enum { readonly isSucceed: boolean; readonly asSucceed: EvmCoreErrorExitSucceed; @@ -1244,7 +1257,7 @@ readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal'; } - /** @name EvmCoreErrorExitSucceed (110) */ + /** @name EvmCoreErrorExitSucceed (111) */ interface EvmCoreErrorExitSucceed extends Enum { readonly isStopped: boolean; readonly isReturned: boolean; @@ -1252,7 +1265,7 @@ readonly type: 'Stopped' | 'Returned' | 'Suicided'; } - /** @name EvmCoreErrorExitError (111) */ + /** @name EvmCoreErrorExitError (112) */ interface EvmCoreErrorExitError extends Enum { readonly isStackUnderflow: boolean; readonly isStackOverflow: boolean; @@ -1273,13 +1286,13 @@ readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'InvalidCode'; } - /** @name EvmCoreErrorExitRevert (114) */ + /** @name EvmCoreErrorExitRevert (115) */ interface EvmCoreErrorExitRevert extends Enum { readonly isReverted: boolean; readonly type: 'Reverted'; } - /** @name EvmCoreErrorExitFatal (115) */ + /** @name EvmCoreErrorExitFatal (116) */ interface EvmCoreErrorExitFatal extends Enum { readonly isNotSupported: boolean; readonly isUnhandledInterrupt: boolean; @@ -1290,7 +1303,18 @@ readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other'; } - /** @name FrameSystemPhase (116) */ + /** @name PalletEvmContractHelpersEvent (117) */ + interface PalletEvmContractHelpersEvent extends Enum { + readonly isContractSponsorSet: boolean; + readonly asContractSponsorSet: ITuple<[H160, AccountId32]>; + readonly isContractSponsorshipConfirmed: boolean; + readonly asContractSponsorshipConfirmed: ITuple<[H160, AccountId32]>; + readonly isContractSponsorRemoved: boolean; + readonly asContractSponsorRemoved: H160; + readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved'; + } + + /** @name FrameSystemPhase (118) */ interface FrameSystemPhase extends Enum { readonly isApplyExtrinsic: boolean; readonly asApplyExtrinsic: u32; @@ -1299,13 +1323,13 @@ readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization'; } - /** @name FrameSystemLastRuntimeUpgradeInfo (118) */ + /** @name FrameSystemLastRuntimeUpgradeInfo (120) */ interface FrameSystemLastRuntimeUpgradeInfo extends Struct { readonly specVersion: Compact; readonly specName: Text; } - /** @name FrameSystemCall (119) */ + /** @name FrameSystemCall (121) */ interface FrameSystemCall extends Enum { readonly isFillBlock: boolean; readonly asFillBlock: { @@ -1347,21 +1371,21 @@ readonly type: 'FillBlock' | 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent'; } - /** @name FrameSystemLimitsBlockWeights (124) */ + /** @name FrameSystemLimitsBlockWeights (126) */ interface FrameSystemLimitsBlockWeights extends Struct { readonly baseBlock: u64; readonly maxBlock: u64; readonly perClass: FrameSupportWeightsPerDispatchClassWeightsPerClass; } - /** @name FrameSupportWeightsPerDispatchClassWeightsPerClass (125) */ + /** @name FrameSupportWeightsPerDispatchClassWeightsPerClass (127) */ interface FrameSupportWeightsPerDispatchClassWeightsPerClass extends Struct { readonly normal: FrameSystemLimitsWeightsPerClass; readonly operational: FrameSystemLimitsWeightsPerClass; readonly mandatory: FrameSystemLimitsWeightsPerClass; } - /** @name FrameSystemLimitsWeightsPerClass (126) */ + /** @name FrameSystemLimitsWeightsPerClass (128) */ interface FrameSystemLimitsWeightsPerClass extends Struct { readonly baseExtrinsic: u64; readonly maxExtrinsic: Option; @@ -1369,25 +1393,25 @@ readonly reserved: Option; } - /** @name FrameSystemLimitsBlockLength (128) */ + /** @name FrameSystemLimitsBlockLength (130) */ interface FrameSystemLimitsBlockLength extends Struct { readonly max: FrameSupportWeightsPerDispatchClassU32; } - /** @name FrameSupportWeightsPerDispatchClassU32 (129) */ + /** @name FrameSupportWeightsPerDispatchClassU32 (131) */ interface FrameSupportWeightsPerDispatchClassU32 extends Struct { readonly normal: u32; readonly operational: u32; readonly mandatory: u32; } - /** @name FrameSupportWeightsRuntimeDbWeight (130) */ + /** @name FrameSupportWeightsRuntimeDbWeight (132) */ interface FrameSupportWeightsRuntimeDbWeight extends Struct { readonly read: u64; readonly write: u64; } - /** @name SpVersionRuntimeVersion (131) */ + /** @name SpVersionRuntimeVersion (133) */ interface SpVersionRuntimeVersion extends Struct { readonly specName: Text; readonly implName: Text; @@ -1399,7 +1423,7 @@ readonly stateVersion: u8; } - /** @name FrameSystemError (136) */ + /** @name FrameSystemError (138) */ interface FrameSystemError extends Enum { readonly isInvalidSpecName: boolean; readonly isSpecVersionNeedsToIncrease: boolean; @@ -1410,7 +1434,7 @@ readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered'; } - /** @name PolkadotPrimitivesV2PersistedValidationData (137) */ + /** @name PolkadotPrimitivesV2PersistedValidationData (139) */ interface PolkadotPrimitivesV2PersistedValidationData extends Struct { readonly parentHead: Bytes; readonly relayParentNumber: u32; @@ -1418,18 +1442,18 @@ readonly maxPovSize: u32; } - /** @name PolkadotPrimitivesV2UpgradeRestriction (140) */ + /** @name PolkadotPrimitivesV2UpgradeRestriction (142) */ interface PolkadotPrimitivesV2UpgradeRestriction extends Enum { readonly isPresent: boolean; readonly type: 'Present'; } - /** @name SpTrieStorageProof (141) */ + /** @name SpTrieStorageProof (143) */ interface SpTrieStorageProof extends Struct { readonly trieNodes: BTreeSet; } - /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (143) */ + /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (145) */ interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct { readonly dmqMqcHead: H256; readonly relayDispatchQueueSize: ITuple<[u32, u32]>; @@ -1437,7 +1461,7 @@ readonly egressChannels: Vec>; } - /** @name PolkadotPrimitivesV2AbridgedHrmpChannel (146) */ + /** @name PolkadotPrimitivesV2AbridgedHrmpChannel (148) */ interface PolkadotPrimitivesV2AbridgedHrmpChannel extends Struct { readonly maxCapacity: u32; readonly maxTotalSize: u32; @@ -1447,7 +1471,7 @@ readonly mqcHead: Option; } - /** @name PolkadotPrimitivesV2AbridgedHostConfiguration (147) */ + /** @name PolkadotPrimitivesV2AbridgedHostConfiguration (149) */ interface PolkadotPrimitivesV2AbridgedHostConfiguration extends Struct { readonly maxCodeSize: u32; readonly maxHeadDataSize: u32; @@ -1460,13 +1484,13 @@ readonly validationUpgradeDelay: u32; } - /** @name PolkadotCorePrimitivesOutboundHrmpMessage (153) */ + /** @name PolkadotCorePrimitivesOutboundHrmpMessage (155) */ interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct { readonly recipient: u32; readonly data: Bytes; } - /** @name CumulusPalletParachainSystemCall (154) */ + /** @name CumulusPalletParachainSystemCall (156) */ interface CumulusPalletParachainSystemCall extends Enum { readonly isSetValidationData: boolean; readonly asSetValidationData: { @@ -1487,7 +1511,7 @@ readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade'; } - /** @name CumulusPrimitivesParachainInherentParachainInherentData (155) */ + /** @name CumulusPrimitivesParachainInherentParachainInherentData (157) */ interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct { readonly validationData: PolkadotPrimitivesV2PersistedValidationData; readonly relayChainState: SpTrieStorageProof; @@ -1495,19 +1519,19 @@ readonly horizontalMessages: BTreeMap>; } - /** @name PolkadotCorePrimitivesInboundDownwardMessage (157) */ + /** @name PolkadotCorePrimitivesInboundDownwardMessage (159) */ interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct { readonly sentAt: u32; readonly msg: Bytes; } - /** @name PolkadotCorePrimitivesInboundHrmpMessage (160) */ + /** @name PolkadotCorePrimitivesInboundHrmpMessage (162) */ interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct { readonly sentAt: u32; readonly data: Bytes; } - /** @name CumulusPalletParachainSystemError (163) */ + /** @name CumulusPalletParachainSystemError (165) */ interface CumulusPalletParachainSystemError extends Enum { readonly isOverlappingUpgrades: boolean; readonly isProhibitedByPolkadot: boolean; @@ -1520,14 +1544,14 @@ readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized'; } - /** @name PalletBalancesBalanceLock (165) */ + /** @name PalletBalancesBalanceLock (167) */ interface PalletBalancesBalanceLock extends Struct { readonly id: U8aFixed; readonly amount: u128; readonly reasons: PalletBalancesReasons; } - /** @name PalletBalancesReasons (166) */ + /** @name PalletBalancesReasons (168) */ interface PalletBalancesReasons extends Enum { readonly isFee: boolean; readonly isMisc: boolean; @@ -1535,20 +1559,20 @@ readonly type: 'Fee' | 'Misc' | 'All'; } - /** @name PalletBalancesReserveData (169) */ + /** @name PalletBalancesReserveData (171) */ interface PalletBalancesReserveData extends Struct { readonly id: U8aFixed; readonly amount: u128; } - /** @name PalletBalancesReleases (171) */ + /** @name PalletBalancesReleases (173) */ interface PalletBalancesReleases extends Enum { readonly isV100: boolean; readonly isV200: boolean; readonly type: 'V100' | 'V200'; } - /** @name PalletBalancesCall (172) */ + /** @name PalletBalancesCall (174) */ interface PalletBalancesCall extends Enum { readonly isTransfer: boolean; readonly asTransfer: { @@ -1585,7 +1609,7 @@ readonly type: 'Transfer' | 'SetBalance' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve'; } - /** @name PalletBalancesError (175) */ + /** @name PalletBalancesError (177) */ interface PalletBalancesError extends Enum { readonly isVestingBalance: boolean; readonly isLiquidityRestrictions: boolean; @@ -1598,7 +1622,7 @@ readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'KeepAlive' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves'; } - /** @name PalletTimestampCall (177) */ + /** @name PalletTimestampCall (179) */ interface PalletTimestampCall extends Enum { readonly isSet: boolean; readonly asSet: { @@ -1607,14 +1631,14 @@ readonly type: 'Set'; } - /** @name PalletTransactionPaymentReleases (179) */ + /** @name PalletTransactionPaymentReleases (181) */ interface PalletTransactionPaymentReleases extends Enum { readonly isV1Ancient: boolean; readonly isV2: boolean; readonly type: 'V1Ancient' | 'V2'; } - /** @name PalletTreasuryProposal (180) */ + /** @name PalletTreasuryProposal (182) */ interface PalletTreasuryProposal extends Struct { readonly proposer: AccountId32; readonly value: u128; @@ -1622,7 +1646,7 @@ readonly bond: u128; } - /** @name PalletTreasuryCall (183) */ + /** @name PalletTreasuryCall (185) */ interface PalletTreasuryCall extends Enum { readonly isProposeSpend: boolean; readonly asProposeSpend: { @@ -1649,10 +1673,10 @@ readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval'; } - /** @name FrameSupportPalletId (186) */ + /** @name FrameSupportPalletId (188) */ interface FrameSupportPalletId extends U8aFixed {} - /** @name PalletTreasuryError (187) */ + /** @name PalletTreasuryError (189) */ interface PalletTreasuryError extends Enum { readonly isInsufficientProposersBalance: boolean; readonly isInvalidIndex: boolean; @@ -1662,7 +1686,7 @@ readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved'; } - /** @name PalletSudoCall (188) */ + /** @name PalletSudoCall (190) */ interface PalletSudoCall extends Enum { readonly isSudo: boolean; readonly asSudo: { @@ -1685,7 +1709,7 @@ readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs'; } - /** @name OrmlVestingModuleCall (190) */ + /** @name OrmlVestingModuleCall (192) */ interface OrmlVestingModuleCall extends Enum { readonly isClaim: boolean; readonly isVestedTransfer: boolean; @@ -1705,7 +1729,7 @@ readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor'; } - /** @name CumulusPalletXcmpQueueCall (192) */ + /** @name CumulusPalletXcmpQueueCall (194) */ interface CumulusPalletXcmpQueueCall extends Enum { readonly isServiceOverweight: boolean; readonly asServiceOverweight: { @@ -1741,7 +1765,7 @@ readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight'; } - /** @name PalletXcmCall (193) */ + /** @name PalletXcmCall (195) */ interface PalletXcmCall extends Enum { readonly isSend: boolean; readonly asSend: { @@ -1803,7 +1827,7 @@ readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets'; } - /** @name XcmVersionedXcm (194) */ + /** @name XcmVersionedXcm (196) */ interface XcmVersionedXcm extends Enum { readonly isV0: boolean; readonly asV0: XcmV0Xcm; @@ -1814,7 +1838,7 @@ readonly type: 'V0' | 'V1' | 'V2'; } - /** @name XcmV0Xcm (195) */ + /** @name XcmV0Xcm (197) */ interface XcmV0Xcm extends Enum { readonly isWithdrawAsset: boolean; readonly asWithdrawAsset: { @@ -1877,7 +1901,7 @@ readonly type: 'WithdrawAsset' | 'ReserveAssetDeposit' | 'TeleportAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'RelayedFrom'; } - /** @name XcmV0Order (197) */ + /** @name XcmV0Order (199) */ interface XcmV0Order extends Enum { readonly isNull: boolean; readonly isDepositAsset: boolean; @@ -1925,14 +1949,14 @@ readonly type: 'Null' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution'; } - /** @name XcmV0Response (199) */ + /** @name XcmV0Response (201) */ interface XcmV0Response extends Enum { readonly isAssets: boolean; readonly asAssets: Vec; readonly type: 'Assets'; } - /** @name XcmV1Xcm (200) */ + /** @name XcmV1Xcm (202) */ interface XcmV1Xcm extends Enum { readonly isWithdrawAsset: boolean; readonly asWithdrawAsset: { @@ -2001,7 +2025,7 @@ readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'RelayedFrom' | 'SubscribeVersion' | 'UnsubscribeVersion'; } - /** @name XcmV1Order (202) */ + /** @name XcmV1Order (204) */ interface XcmV1Order extends Enum { readonly isNoop: boolean; readonly isDepositAsset: boolean; @@ -2051,7 +2075,7 @@ readonly type: 'Noop' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution'; } - /** @name XcmV1Response (204) */ + /** @name XcmV1Response (206) */ interface XcmV1Response extends Enum { readonly isAssets: boolean; readonly asAssets: XcmV1MultiassetMultiAssets; @@ -2060,10 +2084,10 @@ readonly type: 'Assets' | 'Version'; } - /** @name CumulusPalletXcmCall (218) */ + /** @name CumulusPalletXcmCall (220) */ type CumulusPalletXcmCall = Null; - /** @name CumulusPalletDmpQueueCall (219) */ + /** @name CumulusPalletDmpQueueCall (221) */ interface CumulusPalletDmpQueueCall extends Enum { readonly isServiceOverweight: boolean; readonly asServiceOverweight: { @@ -2073,7 +2097,7 @@ readonly type: 'ServiceOverweight'; } - /** @name PalletInflationCall (220) */ + /** @name PalletInflationCall (222) */ interface PalletInflationCall extends Enum { readonly isStartInflation: boolean; readonly asStartInflation: { @@ -2082,7 +2106,7 @@ readonly type: 'StartInflation'; } - /** @name PalletUniqueCall (221) */ + /** @name PalletUniqueCall (223) */ interface PalletUniqueCall extends Enum { readonly isCreateCollection: boolean; readonly asCreateCollection: { @@ -2240,7 +2264,7 @@ readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition'; } - /** @name UpDataStructsCollectionMode (226) */ + /** @name UpDataStructsCollectionMode (228) */ interface UpDataStructsCollectionMode extends Enum { readonly isNft: boolean; readonly isFungible: boolean; @@ -2249,7 +2273,7 @@ readonly type: 'Nft' | 'Fungible' | 'ReFungible'; } - /** @name UpDataStructsCreateCollectionData (227) */ + /** @name UpDataStructsCreateCollectionData (229) */ interface UpDataStructsCreateCollectionData extends Struct { readonly mode: UpDataStructsCollectionMode; readonly access: Option; @@ -2263,14 +2287,14 @@ readonly properties: Vec; } - /** @name UpDataStructsAccessMode (229) */ + /** @name UpDataStructsAccessMode (231) */ interface UpDataStructsAccessMode extends Enum { readonly isNormal: boolean; readonly isAllowList: boolean; readonly type: 'Normal' | 'AllowList'; } - /** @name UpDataStructsCollectionLimits (231) */ + /** @name UpDataStructsCollectionLimits (233) */ interface UpDataStructsCollectionLimits extends Struct { readonly accountTokenOwnershipLimit: Option; readonly sponsoredDataSize: Option; @@ -2283,7 +2307,7 @@ readonly transfersEnabled: Option; } - /** @name UpDataStructsSponsoringRateLimit (233) */ + /** @name UpDataStructsSponsoringRateLimit (235) */ interface UpDataStructsSponsoringRateLimit extends Enum { readonly isSponsoringDisabled: boolean; readonly isBlocks: boolean; @@ -2291,43 +2315,43 @@ readonly type: 'SponsoringDisabled' | 'Blocks'; } - /** @name UpDataStructsCollectionPermissions (236) */ + /** @name UpDataStructsCollectionPermissions (238) */ interface UpDataStructsCollectionPermissions extends Struct { readonly access: Option; readonly mintMode: Option; readonly nesting: Option; } - /** @name UpDataStructsNestingPermissions (238) */ + /** @name UpDataStructsNestingPermissions (240) */ interface UpDataStructsNestingPermissions extends Struct { readonly tokenOwner: bool; readonly collectionAdmin: bool; readonly restricted: Option; } - /** @name UpDataStructsOwnerRestrictedSet (240) */ + /** @name UpDataStructsOwnerRestrictedSet (242) */ interface UpDataStructsOwnerRestrictedSet extends BTreeSet {} - /** @name UpDataStructsPropertyKeyPermission (245) */ + /** @name UpDataStructsPropertyKeyPermission (247) */ interface UpDataStructsPropertyKeyPermission extends Struct { readonly key: Bytes; readonly permission: UpDataStructsPropertyPermission; } - /** @name UpDataStructsPropertyPermission (246) */ + /** @name UpDataStructsPropertyPermission (248) */ interface UpDataStructsPropertyPermission extends Struct { readonly mutable: bool; readonly collectionAdmin: bool; readonly tokenOwner: bool; } - /** @name UpDataStructsProperty (249) */ + /** @name UpDataStructsProperty (251) */ interface UpDataStructsProperty extends Struct { readonly key: Bytes; readonly value: Bytes; } - /** @name UpDataStructsCreateItemData (252) */ + /** @name UpDataStructsCreateItemData (254) */ interface UpDataStructsCreateItemData extends Enum { readonly isNft: boolean; readonly asNft: UpDataStructsCreateNftData; @@ -2338,23 +2362,23 @@ readonly type: 'Nft' | 'Fungible' | 'ReFungible'; } - /** @name UpDataStructsCreateNftData (253) */ + /** @name UpDataStructsCreateNftData (255) */ interface UpDataStructsCreateNftData extends Struct { readonly properties: Vec; } - /** @name UpDataStructsCreateFungibleData (254) */ + /** @name UpDataStructsCreateFungibleData (256) */ interface UpDataStructsCreateFungibleData extends Struct { readonly value: u128; } - /** @name UpDataStructsCreateReFungibleData (255) */ + /** @name UpDataStructsCreateReFungibleData (257) */ interface UpDataStructsCreateReFungibleData extends Struct { readonly pieces: u128; readonly properties: Vec; } - /** @name UpDataStructsCreateItemExData (258) */ + /** @name UpDataStructsCreateItemExData (260) */ interface UpDataStructsCreateItemExData extends Enum { readonly isNft: boolean; readonly asNft: Vec; @@ -2367,26 +2391,26 @@ readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners'; } - /** @name UpDataStructsCreateNftExData (260) */ + /** @name UpDataStructsCreateNftExData (262) */ interface UpDataStructsCreateNftExData extends Struct { readonly properties: Vec; readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; } - /** @name UpDataStructsCreateRefungibleExSingleOwner (267) */ + /** @name UpDataStructsCreateRefungibleExSingleOwner (269) */ interface UpDataStructsCreateRefungibleExSingleOwner extends Struct { readonly user: PalletEvmAccountBasicCrossAccountIdRepr; readonly pieces: u128; readonly properties: Vec; } - /** @name UpDataStructsCreateRefungibleExMultipleOwners (269) */ + /** @name UpDataStructsCreateRefungibleExMultipleOwners (271) */ interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct { readonly users: BTreeMap; readonly properties: Vec; } - /** @name PalletUniqueSchedulerCall (270) */ + /** @name PalletUniqueSchedulerCall (272) */ interface PalletUniqueSchedulerCall extends Enum { readonly isScheduleNamed: boolean; readonly asScheduleNamed: { @@ -2411,7 +2435,7 @@ readonly type: 'ScheduleNamed' | 'CancelNamed' | 'ScheduleNamedAfter'; } - /** @name FrameSupportScheduleMaybeHashed (272) */ + /** @name FrameSupportScheduleMaybeHashed (274) */ interface FrameSupportScheduleMaybeHashed extends Enum { readonly isValue: boolean; readonly asValue: Call; @@ -2420,7 +2444,7 @@ readonly type: 'Value' | 'Hash'; } - /** @name PalletConfigurationCall (273) */ + /** @name PalletConfigurationCall (275) */ interface PalletConfigurationCall extends Enum { readonly isSetWeightToFeeCoefficientOverride: boolean; readonly asSetWeightToFeeCoefficientOverride: { @@ -2433,13 +2457,13 @@ readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride'; } - /** @name PalletTemplateTransactionPaymentCall (274) */ + /** @name PalletTemplateTransactionPaymentCall (276) */ type PalletTemplateTransactionPaymentCall = Null; - /** @name PalletStructureCall (275) */ + /** @name PalletStructureCall (277) */ type PalletStructureCall = Null; - /** @name PalletRmrkCoreCall (276) */ + /** @name PalletRmrkCoreCall (278) */ interface PalletRmrkCoreCall extends Enum { readonly isCreateCollection: boolean; readonly asCreateCollection: { @@ -2545,7 +2569,7 @@ readonly type: 'CreateCollection' | 'DestroyCollection' | 'ChangeCollectionIssuer' | 'LockCollection' | 'MintNft' | 'BurnNft' | 'Send' | 'AcceptNft' | 'RejectNft' | 'AcceptResource' | 'AcceptResourceRemoval' | 'SetProperty' | 'SetPriority' | 'AddBasicResource' | 'AddComposableResource' | 'AddSlotResource' | 'RemoveResource'; } - /** @name RmrkTraitsResourceResourceTypes (282) */ + /** @name RmrkTraitsResourceResourceTypes (284) */ interface RmrkTraitsResourceResourceTypes extends Enum { readonly isBasic: boolean; readonly asBasic: RmrkTraitsResourceBasicResource; @@ -2556,7 +2580,7 @@ readonly type: 'Basic' | 'Composable' | 'Slot'; } - /** @name RmrkTraitsResourceBasicResource (284) */ + /** @name RmrkTraitsResourceBasicResource (286) */ interface RmrkTraitsResourceBasicResource extends Struct { readonly src: Option; readonly metadata: Option; @@ -2564,7 +2588,7 @@ readonly thumb: Option; } - /** @name RmrkTraitsResourceComposableResource (286) */ + /** @name RmrkTraitsResourceComposableResource (288) */ interface RmrkTraitsResourceComposableResource extends Struct { readonly parts: Vec; readonly base: u32; @@ -2574,7 +2598,7 @@ readonly thumb: Option; } - /** @name RmrkTraitsResourceSlotResource (287) */ + /** @name RmrkTraitsResourceSlotResource (289) */ interface RmrkTraitsResourceSlotResource extends Struct { readonly base: u32; readonly src: Option; @@ -2584,7 +2608,7 @@ readonly thumb: Option; } - /** @name PalletRmrkEquipCall (290) */ + /** @name PalletRmrkEquipCall (292) */ interface PalletRmrkEquipCall extends Enum { readonly isCreateBase: boolean; readonly asCreateBase: { @@ -2606,7 +2630,7 @@ readonly type: 'CreateBase' | 'ThemeAdd' | 'Equippable'; } - /** @name RmrkTraitsPartPartType (293) */ + /** @name RmrkTraitsPartPartType (295) */ interface RmrkTraitsPartPartType extends Enum { readonly isFixedPart: boolean; readonly asFixedPart: RmrkTraitsPartFixedPart; @@ -2615,14 +2639,14 @@ readonly type: 'FixedPart' | 'SlotPart'; } - /** @name RmrkTraitsPartFixedPart (295) */ + /** @name RmrkTraitsPartFixedPart (297) */ interface RmrkTraitsPartFixedPart extends Struct { readonly id: u32; readonly z: u32; readonly src: Bytes; } - /** @name RmrkTraitsPartSlotPart (296) */ + /** @name RmrkTraitsPartSlotPart (298) */ interface RmrkTraitsPartSlotPart extends Struct { readonly id: u32; readonly equippable: RmrkTraitsPartEquippableList; @@ -2630,7 +2654,7 @@ readonly z: u32; } - /** @name RmrkTraitsPartEquippableList (297) */ + /** @name RmrkTraitsPartEquippableList (299) */ interface RmrkTraitsPartEquippableList extends Enum { readonly isAll: boolean; readonly isEmpty: boolean; @@ -2639,20 +2663,54 @@ readonly type: 'All' | 'Empty' | 'Custom'; } - /** @name RmrkTraitsTheme (299) */ + /** @name RmrkTraitsTheme (301) */ interface RmrkTraitsTheme extends Struct { readonly name: Bytes; readonly properties: Vec; readonly inherit: bool; } - /** @name RmrkTraitsThemeThemeProperty (301) */ + /** @name RmrkTraitsThemeThemeProperty (303) */ interface RmrkTraitsThemeThemeProperty extends Struct { readonly key: Bytes; readonly value: Bytes; } - /** @name PalletEvmCall (303) */ + /** @name PalletAppPromotionCall (305) */ + interface PalletAppPromotionCall extends Enum { + readonly isSetAdminAddress: boolean; + readonly asSetAdminAddress: { + readonly admin: PalletEvmAccountBasicCrossAccountIdRepr; + } & Struct; + readonly isStake: boolean; + readonly asStake: { + readonly amount: u128; + } & Struct; + readonly isUnstake: boolean; + readonly isSponsorCollection: boolean; + readonly asSponsorCollection: { + readonly collectionId: u32; + } & Struct; + readonly isStopSponsoringCollection: boolean; + readonly asStopSponsoringCollection: { + readonly collectionId: u32; + } & Struct; + readonly isSponsorContract: boolean; + readonly asSponsorContract: { + readonly contractId: H160; + } & Struct; + readonly isStopSponsoringContract: boolean; + readonly asStopSponsoringContract: { + readonly contractId: H160; + } & Struct; + readonly isPayoutStakers: boolean; + readonly asPayoutStakers: { + readonly stakersNumber: Option; + } & Struct; + readonly type: 'SetAdminAddress' | 'Stake' | 'Unstake' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers'; + } + + /** @name PalletEvmCall (307) */ interface PalletEvmCall extends Enum { readonly isWithdraw: boolean; readonly asWithdraw: { @@ -2697,7 +2755,7 @@ readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2'; } - /** @name PalletEthereumCall (307) */ + /** @name PalletEthereumCall (311) */ interface PalletEthereumCall extends Enum { readonly isTransact: boolean; readonly asTransact: { @@ -2706,7 +2764,7 @@ readonly type: 'Transact'; } - /** @name EthereumTransactionTransactionV2 (308) */ + /** @name EthereumTransactionTransactionV2 (312) */ interface EthereumTransactionTransactionV2 extends Enum { readonly isLegacy: boolean; readonly asLegacy: EthereumTransactionLegacyTransaction; @@ -2717,7 +2775,7 @@ readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; } - /** @name EthereumTransactionLegacyTransaction (309) */ + /** @name EthereumTransactionLegacyTransaction (313) */ interface EthereumTransactionLegacyTransaction extends Struct { readonly nonce: U256; readonly gasPrice: U256; @@ -2728,7 +2786,7 @@ readonly signature: EthereumTransactionTransactionSignature; } - /** @name EthereumTransactionTransactionAction (310) */ + /** @name EthereumTransactionTransactionAction (314) */ interface EthereumTransactionTransactionAction extends Enum { readonly isCall: boolean; readonly asCall: H160; @@ -2736,14 +2794,14 @@ readonly type: 'Call' | 'Create'; } - /** @name EthereumTransactionTransactionSignature (311) */ + /** @name EthereumTransactionTransactionSignature (315) */ interface EthereumTransactionTransactionSignature extends Struct { readonly v: u64; readonly r: H256; readonly s: H256; } - /** @name EthereumTransactionEip2930Transaction (313) */ + /** @name EthereumTransactionEip2930Transaction (317) */ interface EthereumTransactionEip2930Transaction extends Struct { readonly chainId: u64; readonly nonce: U256; @@ -2758,13 +2816,13 @@ readonly s: H256; } - /** @name EthereumTransactionAccessListItem (315) */ + /** @name EthereumTransactionAccessListItem (319) */ interface EthereumTransactionAccessListItem extends Struct { readonly address: H160; readonly storageKeys: Vec; } - /** @name EthereumTransactionEip1559Transaction (316) */ + /** @name EthereumTransactionEip1559Transaction (320) */ interface EthereumTransactionEip1559Transaction extends Struct { readonly chainId: u64; readonly nonce: U256; @@ -2780,7 +2838,7 @@ readonly s: H256; } - /** @name PalletEvmMigrationCall (317) */ + /** @name PalletEvmMigrationCall (321) */ interface PalletEvmMigrationCall extends Enum { readonly isBegin: boolean; readonly asBegin: { @@ -2799,13 +2857,13 @@ readonly type: 'Begin' | 'SetData' | 'Finish'; } - /** @name PalletSudoError (320) */ + /** @name PalletSudoError (324) */ interface PalletSudoError extends Enum { readonly isRequireSudo: boolean; readonly type: 'RequireSudo'; } - /** @name OrmlVestingModuleError (322) */ + /** @name OrmlVestingModuleError (326) */ interface OrmlVestingModuleError extends Enum { readonly isZeroVestingPeriod: boolean; readonly isZeroVestingPeriodCount: boolean; @@ -2816,21 +2874,21 @@ readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded'; } - /** @name CumulusPalletXcmpQueueInboundChannelDetails (324) */ + /** @name CumulusPalletXcmpQueueInboundChannelDetails (328) */ interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct { readonly sender: u32; readonly state: CumulusPalletXcmpQueueInboundState; readonly messageMetadata: Vec>; } - /** @name CumulusPalletXcmpQueueInboundState (325) */ + /** @name CumulusPalletXcmpQueueInboundState (329) */ interface CumulusPalletXcmpQueueInboundState extends Enum { readonly isOk: boolean; readonly isSuspended: boolean; readonly type: 'Ok' | 'Suspended'; } - /** @name PolkadotParachainPrimitivesXcmpMessageFormat (328) */ + /** @name PolkadotParachainPrimitivesXcmpMessageFormat (332) */ interface PolkadotParachainPrimitivesXcmpMessageFormat extends Enum { readonly isConcatenatedVersionedXcm: boolean; readonly isConcatenatedEncodedBlob: boolean; @@ -2838,7 +2896,7 @@ readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals'; } - /** @name CumulusPalletXcmpQueueOutboundChannelDetails (331) */ + /** @name CumulusPalletXcmpQueueOutboundChannelDetails (335) */ interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct { readonly recipient: u32; readonly state: CumulusPalletXcmpQueueOutboundState; @@ -2847,14 +2905,14 @@ readonly lastIndex: u16; } - /** @name CumulusPalletXcmpQueueOutboundState (332) */ + /** @name CumulusPalletXcmpQueueOutboundState (336) */ interface CumulusPalletXcmpQueueOutboundState extends Enum { readonly isOk: boolean; readonly isSuspended: boolean; readonly type: 'Ok' | 'Suspended'; } - /** @name CumulusPalletXcmpQueueQueueConfigData (334) */ + /** @name CumulusPalletXcmpQueueQueueConfigData (338) */ interface CumulusPalletXcmpQueueQueueConfigData extends Struct { readonly suspendThreshold: u32; readonly dropThreshold: u32; @@ -2864,7 +2922,7 @@ readonly xcmpMaxIndividualWeight: u64; } - /** @name CumulusPalletXcmpQueueError (336) */ + /** @name CumulusPalletXcmpQueueError (340) */ interface CumulusPalletXcmpQueueError extends Enum { readonly isFailedToSend: boolean; readonly isBadXcmOrigin: boolean; @@ -2874,7 +2932,7 @@ readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit'; } - /** @name PalletXcmError (337) */ + /** @name PalletXcmError (341) */ interface PalletXcmError extends Enum { readonly isUnreachable: boolean; readonly isSendFailure: boolean; @@ -2892,29 +2950,29 @@ readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed'; } - /** @name CumulusPalletXcmError (338) */ + /** @name CumulusPalletXcmError (342) */ type CumulusPalletXcmError = Null; - /** @name CumulusPalletDmpQueueConfigData (339) */ + /** @name CumulusPalletDmpQueueConfigData (343) */ interface CumulusPalletDmpQueueConfigData extends Struct { readonly maxIndividual: u64; } - /** @name CumulusPalletDmpQueuePageIndexData (340) */ + /** @name CumulusPalletDmpQueuePageIndexData (344) */ interface CumulusPalletDmpQueuePageIndexData extends Struct { readonly beginUsed: u32; readonly endUsed: u32; readonly overweightCount: u64; } - /** @name CumulusPalletDmpQueueError (343) */ + /** @name CumulusPalletDmpQueueError (347) */ interface CumulusPalletDmpQueueError extends Enum { readonly isUnknown: boolean; readonly isOverLimit: boolean; readonly type: 'Unknown' | 'OverLimit'; } - /** @name PalletUniqueError (347) */ + /** @name PalletUniqueError (351) */ interface PalletUniqueError extends Enum { readonly isCollectionDecimalPointLimitExceeded: boolean; readonly isConfirmUnsetSponsorFail: boolean; @@ -2923,7 +2981,7 @@ readonly type: 'CollectionDecimalPointLimitExceeded' | 'ConfirmUnsetSponsorFail' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection'; } - /** @name PalletUniqueSchedulerScheduledV3 (350) */ + /** @name PalletUniqueSchedulerScheduledV3 (354) */ interface PalletUniqueSchedulerScheduledV3 extends Struct { readonly maybeId: Option; readonly priority: u8; @@ -2932,7 +2990,7 @@ readonly origin: OpalRuntimeOriginCaller; } - /** @name OpalRuntimeOriginCaller (351) */ + /** @name OpalRuntimeOriginCaller (355) */ interface OpalRuntimeOriginCaller extends Enum { readonly isSystem: boolean; readonly asSystem: FrameSupportDispatchRawOrigin; @@ -2946,7 +3004,7 @@ readonly type: 'System' | 'Void' | 'PolkadotXcm' | 'CumulusXcm' | 'Ethereum'; } - /** @name FrameSupportDispatchRawOrigin (352) */ + /** @name FrameSupportDispatchRawOrigin (356) */ interface FrameSupportDispatchRawOrigin extends Enum { readonly isRoot: boolean; readonly isSigned: boolean; @@ -2955,7 +3013,7 @@ readonly type: 'Root' | 'Signed' | 'None'; } - /** @name PalletXcmOrigin (353) */ + /** @name PalletXcmOrigin (357) */ interface PalletXcmOrigin extends Enum { readonly isXcm: boolean; readonly asXcm: XcmV1MultiLocation; @@ -2964,7 +3022,7 @@ readonly type: 'Xcm' | 'Response'; } - /** @name CumulusPalletXcmOrigin (354) */ + /** @name CumulusPalletXcmOrigin (358) */ interface CumulusPalletXcmOrigin extends Enum { readonly isRelay: boolean; readonly isSiblingParachain: boolean; @@ -2972,17 +3030,17 @@ readonly type: 'Relay' | 'SiblingParachain'; } - /** @name PalletEthereumRawOrigin (355) */ + /** @name PalletEthereumRawOrigin (359) */ interface PalletEthereumRawOrigin extends Enum { readonly isEthereumTransaction: boolean; readonly asEthereumTransaction: H160; readonly type: 'EthereumTransaction'; } - /** @name SpCoreVoid (356) */ + /** @name SpCoreVoid (360) */ type SpCoreVoid = Null; - /** @name PalletUniqueSchedulerError (357) */ + /** @name PalletUniqueSchedulerError (361) */ interface PalletUniqueSchedulerError extends Enum { readonly isFailedToSchedule: boolean; readonly isNotFound: boolean; @@ -2991,21 +3049,21 @@ readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange'; } - /** @name UpDataStructsCollection (358) */ + /** @name UpDataStructsCollection (362) */ interface UpDataStructsCollection extends Struct { readonly owner: AccountId32; readonly mode: UpDataStructsCollectionMode; readonly name: Vec; readonly description: Vec; readonly tokenPrefix: Bytes; - readonly sponsorship: UpDataStructsSponsorshipState; + readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; readonly limits: UpDataStructsCollectionLimits; readonly permissions: UpDataStructsCollectionPermissions; readonly externalCollection: bool; } - /** @name UpDataStructsSponsorshipState (359) */ - interface UpDataStructsSponsorshipState extends Enum { + /** @name UpDataStructsSponsorshipStateAccountId32 (363) */ + interface UpDataStructsSponsorshipStateAccountId32 extends Enum { readonly isDisabled: boolean; readonly isUnconfirmed: boolean; readonly asUnconfirmed: AccountId32; @@ -3014,50 +3072,50 @@ readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; } - /** @name UpDataStructsProperties (360) */ + /** @name UpDataStructsProperties (364) */ interface UpDataStructsProperties extends Struct { readonly map: UpDataStructsPropertiesMapBoundedVec; readonly consumedSpace: u32; readonly spaceLimit: u32; } - /** @name UpDataStructsPropertiesMapBoundedVec (361) */ + /** @name UpDataStructsPropertiesMapBoundedVec (365) */ interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap {} - /** @name UpDataStructsPropertiesMapPropertyPermission (366) */ + /** @name UpDataStructsPropertiesMapPropertyPermission (370) */ interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap {} - /** @name UpDataStructsCollectionStats (373) */ + /** @name UpDataStructsCollectionStats (377) */ interface UpDataStructsCollectionStats extends Struct { readonly created: u32; readonly destroyed: u32; readonly alive: u32; } - /** @name UpDataStructsTokenChild (374) */ + /** @name UpDataStructsTokenChild (378) */ interface UpDataStructsTokenChild extends Struct { readonly token: u32; readonly collection: u32; } - /** @name PhantomTypeUpDataStructs (375) */ + /** @name PhantomTypeUpDataStructs (379) */ interface PhantomTypeUpDataStructs extends Vec> {} - /** @name UpDataStructsTokenData (377) */ + /** @name UpDataStructsTokenData (381) */ interface UpDataStructsTokenData extends Struct { readonly properties: Vec; readonly owner: Option; readonly pieces: u128; } - /** @name UpDataStructsRpcCollection (379) */ + /** @name UpDataStructsRpcCollection (383) */ interface UpDataStructsRpcCollection extends Struct { readonly owner: AccountId32; readonly mode: UpDataStructsCollectionMode; readonly name: Vec; readonly description: Vec; readonly tokenPrefix: Bytes; - readonly sponsorship: UpDataStructsSponsorshipState; + readonly sponsorship: UpDataStructsSponsorshipStateAccountId32; readonly limits: UpDataStructsCollectionLimits; readonly permissions: UpDataStructsCollectionPermissions; readonly tokenPropertyPermissions: Vec; @@ -3065,7 +3123,7 @@ readonly readOnly: bool; } - /** @name RmrkTraitsCollectionCollectionInfo (380) */ + /** @name RmrkTraitsCollectionCollectionInfo (384) */ interface RmrkTraitsCollectionCollectionInfo extends Struct { readonly issuer: AccountId32; readonly metadata: Bytes; @@ -3074,7 +3132,7 @@ readonly nftsCount: u32; } - /** @name RmrkTraitsNftNftInfo (381) */ + /** @name RmrkTraitsNftNftInfo (385) */ interface RmrkTraitsNftNftInfo extends Struct { readonly owner: RmrkTraitsNftAccountIdOrCollectionNftTuple; readonly royalty: Option; @@ -3083,13 +3141,13 @@ readonly pending: bool; } - /** @name RmrkTraitsNftRoyaltyInfo (383) */ + /** @name RmrkTraitsNftRoyaltyInfo (387) */ interface RmrkTraitsNftRoyaltyInfo extends Struct { readonly recipient: AccountId32; readonly amount: Permill; } - /** @name RmrkTraitsResourceResourceInfo (384) */ + /** @name RmrkTraitsResourceResourceInfo (388) */ interface RmrkTraitsResourceResourceInfo extends Struct { readonly id: u32; readonly resource: RmrkTraitsResourceResourceTypes; @@ -3097,26 +3155,26 @@ readonly pendingRemoval: bool; } - /** @name RmrkTraitsPropertyPropertyInfo (385) */ + /** @name RmrkTraitsPropertyPropertyInfo (389) */ interface RmrkTraitsPropertyPropertyInfo extends Struct { readonly key: Bytes; readonly value: Bytes; } - /** @name RmrkTraitsBaseBaseInfo (386) */ + /** @name RmrkTraitsBaseBaseInfo (390) */ interface RmrkTraitsBaseBaseInfo extends Struct { readonly issuer: AccountId32; readonly baseType: Bytes; readonly symbol: Bytes; } - /** @name RmrkTraitsNftNftChild (387) */ + /** @name RmrkTraitsNftNftChild (391) */ interface RmrkTraitsNftNftChild extends Struct { readonly collectionId: u32; readonly nftId: u32; } - /** @name PalletCommonError (389) */ + /** @name PalletCommonError (393) */ interface PalletCommonError extends Enum { readonly isCollectionNotFound: boolean; readonly isMustBeTokenOwner: boolean; @@ -3155,7 +3213,7 @@ readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal'; } - /** @name PalletFungibleError (391) */ + /** @name PalletFungibleError (395) */ interface PalletFungibleError extends Enum { readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean; readonly isFungibleItemsHaveNoId: boolean; @@ -3165,12 +3223,12 @@ readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsHaveNoId' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; } - /** @name PalletRefungibleItemData (392) */ + /** @name PalletRefungibleItemData (396) */ interface PalletRefungibleItemData extends Struct { readonly constData: Bytes; } - /** @name PalletRefungibleError (397) */ + /** @name PalletRefungibleError (401) */ interface PalletRefungibleError extends Enum { readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean; readonly isWrongRefungiblePieces: boolean; @@ -3180,20 +3238,19 @@ readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; } - /** @name PalletNonfungibleItemData (398) */ + /** @name PalletNonfungibleItemData (402) */ interface PalletNonfungibleItemData extends Struct { readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; } - /** @name UpDataStructsPropertyScope (400) */ + /** @name UpDataStructsPropertyScope (404) */ interface UpDataStructsPropertyScope extends Enum { readonly isNone: boolean; readonly isRmrk: boolean; - readonly isEth: boolean; - readonly type: 'None' | 'Rmrk' | 'Eth'; + readonly type: 'None' | 'Rmrk'; } - /** @name PalletNonfungibleError (402) */ + /** @name PalletNonfungibleError (406) */ interface PalletNonfungibleError extends Enum { readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean; readonly isNonfungibleItemsHaveNoAmount: boolean; @@ -3201,7 +3258,7 @@ readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren'; } - /** @name PalletStructureError (403) */ + /** @name PalletStructureError (407) */ interface PalletStructureError extends Enum { readonly isOuroborosDetected: boolean; readonly isDepthLimit: boolean; @@ -3210,7 +3267,7 @@ readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound'; } - /** @name PalletRmrkCoreError (404) */ + /** @name PalletRmrkCoreError (408) */ interface PalletRmrkCoreError extends Enum { readonly isCorruptedCollectionType: boolean; readonly isRmrkPropertyKeyIsTooLong: boolean; @@ -3234,7 +3291,7 @@ readonly type: 'CorruptedCollectionType' | 'RmrkPropertyKeyIsTooLong' | 'RmrkPropertyValueIsTooLong' | 'RmrkPropertyIsNotFound' | 'UnableToDecodeRmrkData' | 'CollectionNotEmpty' | 'NoAvailableCollectionId' | 'NoAvailableNftId' | 'CollectionUnknown' | 'NoPermission' | 'NonTransferable' | 'CollectionFullOrLocked' | 'ResourceDoesntExist' | 'CannotSendToDescendentOrSelf' | 'CannotAcceptNonOwnedNft' | 'CannotRejectNonOwnedNft' | 'CannotRejectNonPendingNft' | 'ResourceNotPending' | 'NoAvailableResourceId'; } - /** @name PalletRmrkEquipError (406) */ + /** @name PalletRmrkEquipError (410) */ interface PalletRmrkEquipError extends Enum { readonly isPermissionError: boolean; readonly isNoAvailableBaseId: boolean; @@ -3246,7 +3303,18 @@ readonly type: 'PermissionError' | 'NoAvailableBaseId' | 'NoAvailablePartId' | 'BaseDoesntExist' | 'NeedsDefaultThemeFirst' | 'PartDoesntExist' | 'NoEquippableOnFixedPart'; } - /** @name PalletEvmError (409) */ + /** @name PalletAppPromotionError (416) */ + interface PalletAppPromotionError extends Enum { + readonly isAdminNotSet: boolean; + readonly isNoPermission: boolean; + readonly isNotSufficientFunds: boolean; + readonly isPendingForBlockOverflow: boolean; + readonly isSponsorNotSet: boolean; + readonly isIncorrectLockedBalanceOperation: boolean; + readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'IncorrectLockedBalanceOperation'; + } + + /** @name PalletEvmError (419) */ interface PalletEvmError extends Enum { readonly isBalanceLow: boolean; readonly isFeeOverflow: boolean; @@ -3257,7 +3325,7 @@ readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce'; } - /** @name FpRpcTransactionStatus (412) */ + /** @name FpRpcTransactionStatus (422) */ interface FpRpcTransactionStatus extends Struct { readonly transactionHash: H256; readonly transactionIndex: u32; @@ -3268,10 +3336,10 @@ readonly logsBloom: EthbloomBloom; } - /** @name EthbloomBloom (414) */ + /** @name EthbloomBloom (424) */ interface EthbloomBloom extends U8aFixed {} - /** @name EthereumReceiptReceiptV3 (416) */ + /** @name EthereumReceiptReceiptV3 (426) */ interface EthereumReceiptReceiptV3 extends Enum { readonly isLegacy: boolean; readonly asLegacy: EthereumReceiptEip658ReceiptData; @@ -3282,7 +3350,7 @@ readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; } - /** @name EthereumReceiptEip658ReceiptData (417) */ + /** @name EthereumReceiptEip658ReceiptData (427) */ interface EthereumReceiptEip658ReceiptData extends Struct { readonly statusCode: u8; readonly usedGas: U256; @@ -3290,14 +3358,14 @@ readonly logs: Vec; } - /** @name EthereumBlock (418) */ + /** @name EthereumBlock (428) */ interface EthereumBlock extends Struct { readonly header: EthereumHeader; readonly transactions: Vec; readonly ommers: Vec; } - /** @name EthereumHeader (419) */ + /** @name EthereumHeader (429) */ interface EthereumHeader extends Struct { readonly parentHash: H256; readonly ommersHash: H256; @@ -3316,24 +3384,34 @@ readonly nonce: EthereumTypesHashH64; } - /** @name EthereumTypesHashH64 (420) */ + /** @name EthereumTypesHashH64 (430) */ interface EthereumTypesHashH64 extends U8aFixed {} - /** @name PalletEthereumError (425) */ + /** @name PalletEthereumError (435) */ interface PalletEthereumError extends Enum { readonly isInvalidSignature: boolean; readonly isPreLogExists: boolean; readonly type: 'InvalidSignature' | 'PreLogExists'; } - /** @name PalletEvmCoderSubstrateError (426) */ + /** @name PalletEvmCoderSubstrateError (436) */ interface PalletEvmCoderSubstrateError extends Enum { readonly isOutOfGas: boolean; readonly isOutOfFund: boolean; readonly type: 'OutOfGas' | 'OutOfFund'; } - /** @name PalletEvmContractHelpersSponsoringModeT (427) */ + /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (437) */ + interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum { + readonly isDisabled: boolean; + readonly isUnconfirmed: boolean; + readonly asUnconfirmed: PalletEvmAccountBasicCrossAccountIdRepr; + readonly isConfirmed: boolean; + readonly asConfirmed: PalletEvmAccountBasicCrossAccountIdRepr; + readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed'; + } + + /** @name PalletEvmContractHelpersSponsoringModeT (438) */ interface PalletEvmContractHelpersSponsoringModeT extends Enum { readonly isDisabled: boolean; readonly isAllowlisted: boolean; @@ -3341,20 +3419,21 @@ readonly type: 'Disabled' | 'Allowlisted' | 'Generous'; } - /** @name PalletEvmContractHelpersError (429) */ + /** @name PalletEvmContractHelpersError (440) */ interface PalletEvmContractHelpersError extends Enum { readonly isNoPermission: boolean; - readonly type: 'NoPermission'; + readonly isNoPendingSponsor: boolean; + readonly type: 'NoPermission' | 'NoPendingSponsor'; } - /** @name PalletEvmMigrationError (430) */ + /** @name PalletEvmMigrationError (441) */ interface PalletEvmMigrationError extends Enum { readonly isAccountNotEmpty: boolean; readonly isAccountIsNotMigrating: boolean; readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating'; } - /** @name SpRuntimeMultiSignature (432) */ + /** @name SpRuntimeMultiSignature (443) */ interface SpRuntimeMultiSignature extends Enum { readonly isEd25519: boolean; readonly asEd25519: SpCoreEd25519Signature; @@ -3365,34 +3444,34 @@ readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa'; } - /** @name SpCoreEd25519Signature (433) */ + /** @name SpCoreEd25519Signature (444) */ interface SpCoreEd25519Signature extends U8aFixed {} - /** @name SpCoreSr25519Signature (435) */ + /** @name SpCoreSr25519Signature (446) */ interface SpCoreSr25519Signature extends U8aFixed {} - /** @name SpCoreEcdsaSignature (436) */ + /** @name SpCoreEcdsaSignature (447) */ interface SpCoreEcdsaSignature extends U8aFixed {} - /** @name FrameSystemExtensionsCheckSpecVersion (439) */ + /** @name FrameSystemExtensionsCheckSpecVersion (450) */ type FrameSystemExtensionsCheckSpecVersion = Null; - /** @name FrameSystemExtensionsCheckGenesis (440) */ + /** @name FrameSystemExtensionsCheckGenesis (451) */ type FrameSystemExtensionsCheckGenesis = Null; - /** @name FrameSystemExtensionsCheckNonce (443) */ + /** @name FrameSystemExtensionsCheckNonce (454) */ interface FrameSystemExtensionsCheckNonce extends Compact {} - /** @name FrameSystemExtensionsCheckWeight (444) */ + /** @name FrameSystemExtensionsCheckWeight (455) */ type FrameSystemExtensionsCheckWeight = Null; - /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (445) */ + /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (456) */ interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact {} - /** @name OpalRuntimeRuntime (446) */ + /** @name OpalRuntimeRuntime (457) */ type OpalRuntimeRuntime = Null; - /** @name PalletEthereumFakeTransactionFinalizer (447) */ + /** @name PalletEthereumFakeTransactionFinalizer (458) */ type PalletEthereumFakeTransactionFinalizer = Null; } // declare module --- a/tests/src/interfaces/types.ts +++ b/tests/src/interfaces/types.ts @@ -2,5 +2,6 @@ /* eslint-disable */ export * from './unique/types'; +export * from './appPromotion/types'; export * from './rmrk/types'; export * from './default/types'; --- a/tests/src/pallet-presence.test.ts +++ b/tests/src/pallet-presence.test.ts @@ -68,9 +68,10 @@ const refungible = 'refungible'; const scheduler = 'scheduler'; const rmrkPallets = ['rmrkcore', 'rmrkequip']; + const appPromotion = 'apppromotion'; if (chain.eq('OPAL by UNIQUE')) { - requiredPallets.push(refungible, scheduler, ...rmrkPallets); + requiredPallets.push(refungible, scheduler, appPromotion, ...rmrkPallets); } else if (chain.eq('QUARTZ by UNIQUE')) { // Insert Quartz additional pallets here } else if (chain.eq('UNIQUE')) { --- a/tests/src/refungible.test.ts +++ b/tests/src/refungible.test.ts @@ -245,8 +245,8 @@ section: 'common', index: '0x4202', data: [ - collection.collectionId.toString(), - token.tokenId.toString(), + helper.api!.createType('u32', collection.collectionId).toHuman(), + helper.api!.createType('u32', token.tokenId).toHuman(), {Substrate: alice.address}, '100', ], @@ -265,8 +265,8 @@ section: 'common', index: '0x4203', data: [ - collection.collectionId.toString(), - token.tokenId.toString(), + helper.api!.createType('u32', collection.collectionId).toHuman(), + helper.api!.createType('u32', token.tokenId).toHuman(), {Substrate: alice.address}, '50', ], --- a/tests/src/removeCollectionAdmin.test.ts +++ b/tests/src/removeCollectionAdmin.test.ts @@ -27,6 +27,7 @@ const alice = privateKey('//Alice'); const bob = privateKey('//Bob'); const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const collectionInfo = await collection.getData(); expect(collectionInfo?.raw.owner.toString()).to.be.deep.eq(alice.address); // first - add collection admin Bob --- a/tests/src/substrate/substrate-api.ts +++ b/tests/src/substrate/substrate-api.ts @@ -25,6 +25,8 @@ import privateKey from './privateKey'; import promisifySubstrate from './promisify-substrate'; +import {SilentConsole} from '../util/playgrounds/unique.dev'; + function defaultApiOptions(): ApiOptions { @@ -42,6 +44,7 @@ }, rpc: { unique: defs.unique.rpc, + appPromotion: defs.appPromotion.rpc, rmrk: defs.rmrk.rpc, eth: { feeHistory: { @@ -75,25 +78,8 @@ const api: ApiPromise = new ApiPromise(settings); let result: T = null as unknown as T; - // TODO: Remove, this is temporary: Filter unneeded API output - // (Jaco promised it will be removed in the next version) - const consoleErr = console.error; - const consoleLog = console.log; - const consoleWarn = console.warn; - - const outFn = (printer: any) => (...args: any[]) => { - for (const arg of args) { - if (typeof arg !== 'string') - continue; - if (arg.includes('1000:: Normal connection closure') || arg.includes('Not decorating unknown runtime apis:') || arg.includes('RPC methods not decorated:') || arg === 'Normal connection closure') - return; - } - printer(...args); - }; - - console.error = outFn(consoleErr.bind(console)); - console.log = outFn(consoleLog.bind(console)); - console.warn = outFn(consoleWarn.bind(console)); + const silentConsole = new SilentConsole(); + silentConsole.enable(); try { await promisifySubstrate(api, async () => { @@ -106,9 +92,7 @@ })(); } finally { await api.disconnect(); - console.error = consoleErr; - console.log = consoleLog; - console.warn = consoleWarn; + silentConsole.disable(); } return result as T; } --- a/tests/src/util/helpers.ts +++ b/tests/src/util/helpers.ts @@ -17,10 +17,11 @@ import '../interfaces/augment-api-rpc'; import '../interfaces/augment-api-query'; import {ApiPromise} from '@polkadot/api'; -import type {AccountId, EventRecord, Event} from '@polkadot/types/interfaces'; +import type {AccountId, EventRecord, Event, BlockNumber} from '@polkadot/types/interfaces'; import type {GenericEventData} from '@polkadot/types'; import {AnyTuple, IEvent, IKeyringPair} from '@polkadot/types/types'; import {evmToAddress} from '@polkadot/util-crypto'; +import {AnyNumber} from '@polkadot/types-codec/types'; import BN from 'bn.js'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; @@ -48,6 +49,7 @@ Fungible = 'fungible', NFT = 'nonfungible', Scheduler = 'scheduler', + AppPromotion = 'apppromotion', } export async function isUnique(): Promise { @@ -1779,3 +1781,23 @@ itApi.only = (name: string, cb: (apis: { api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair }) => any) => itApi(name, cb, {only: true}); itApi.skip = (name: string, cb: (apis: { api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair }) => any) => itApi(name, cb, {skip: true}); + + +export async function expectSubstrateEventsAtBlock(api: ApiPromise, blockNumber: AnyNumber | BlockNumber, section: string, methods: string[], dryRun = false) { + const blockHash = await api.rpc.chain.getBlockHash(blockNumber); + const subEvents = (await api.query.system.events.at(blockHash)) + .filter(x => x.event.section === section) + .map((x) => x.toHuman()); + const events = methods.map((m) => { + return { + event: { + method: m, + section, + }, + }; + }); + if (!dryRun) { + expect(subEvents).to.be.like(events); + } + return subEvents; +} \ No newline at end of file --- a/tests/src/util/playgrounds/index.ts +++ b/tests/src/util/playgrounds/index.ts @@ -2,39 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 import {IKeyringPair} from '@polkadot/types/types'; +import {Context} from 'mocha'; import config from '../../config'; import '../../interfaces/augment-api-events'; -import {DevUniqueHelper} from './unique.dev'; +import {DevUniqueHelper, SilentLogger, SilentConsole} from './unique.dev'; -class SilentLogger { - log(msg: any, level: any): void { } - level = { - ERROR: 'ERROR' as const, - WARNING: 'WARNING' as const, - INFO: 'INFO' as const, - }; -} export const usingPlaygrounds = async (code: (helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise) => { - // TODO: Remove, this is temporary: Filter unneeded API output - // (Jaco promised it will be removed in the next version) - const consoleErr = console.error; - const consoleLog = console.log; - const consoleWarn = console.warn; + const silentConsole = new SilentConsole(); + silentConsole.enable(); - const outFn = (printer: any) => (...args: any[]) => { - for (const arg of args) { - if (typeof arg !== 'string') - continue; - if (arg.includes('1000:: Normal connection closure') || arg.includes('Not decorating unknown runtime apis:') || arg.includes('RPC methods not decorated:') || arg === 'Normal connection closure') - return; - } - printer(...args); - }; - - console.error = outFn(consoleErr.bind(console)); - console.log = outFn(consoleLog.bind(console)); - console.warn = outFn(consoleWarn.bind(console)); const helper = new DevUniqueHelper(new SilentLogger()); try { @@ -45,8 +22,42 @@ } finally { await helper.disconnect(); - console.error = consoleErr; - console.log = consoleLog; - console.warn = consoleWarn; + silentConsole.disable(); } }; + +export enum Pallets { + Inflation = 'inflation', + RmrkCore = 'rmrkcore', + RmrkEquip = 'rmrkequip', + ReFungible = 'refungible', + Fungible = 'fungible', + NFT = 'nonfungible', + Scheduler = 'scheduler', +} + +export function requirePalletsOrSkip(test: Context, helper: DevUniqueHelper, requiredPallets: string[]) { + const missingPallets = helper.fetchMissingPalletNames(requiredPallets); + + if (missingPallets.length > 0) { + const skipMsg = `\tSkipping test '${test.test?.title}'.\n\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`; + console.warn('\x1b[38:5:208m%s\x1b[0m', skipMsg); + test.skip(); + } +} + +export async function itSub(name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { + (opts.only ? it.only : + opts.skip ? it.skip : it)(name, async function () { + await usingPlaygrounds(async (helper, privateKey) => { + if (opts.requiredPallets) { + requirePalletsOrSkip(this, helper, opts.requiredPallets); + } + + await cb({helper, privateKey}); + }); + }); +} +itSub.only = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itSub(name, cb, {only: true}); +itSub.skip = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itSub(name, cb, {skip: true}); +itSub.ifWithPallets = (name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itSub(name, cb, {requiredPallets: required}); --- a/tests/src/util/playgrounds/types.ts +++ b/tests/src/util/playgrounds/types.ts @@ -101,10 +101,27 @@ tokenId: number; } +export interface IBlock { + extrinsics: IExtrinsic[] + header: { + parentHash: string, + number: number, + }; +} + +export interface IExtrinsic { + isSigned: boolean, + method: { + method: string, + section: string, + args: any[] + } +} + export interface ICollectionCreationOptions { - name: string | number[]; - description: string | number[]; - tokenPrefix: string | number[]; + name?: string | number[]; + description?: string | number[]; + tokenPrefix?: string | number[]; mode?: { nft?: null; refungible?: null; @@ -123,8 +140,15 @@ tokenSymbol: string[] } +export interface ISubstrateBalance { + free: bigint, + reserved: bigint, + miscFrozen: bigint, + feeFrozen: bigint +} + export type TSubstrateAccount = string; export type TEthereumAccount = string; export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated'; export type TUniqueNetworks = 'opal' | 'quartz' | 'unique'; -export type TSigner = IKeyringPair; // | 'string' \ No newline at end of file +export type TSigner = IKeyringPair; // | 'string' --- a/tests/src/util/playgrounds/unique.dev.ts +++ b/tests/src/util/playgrounds/unique.dev.ts @@ -5,22 +5,69 @@ import {UniqueHelper} from './unique'; import {ApiPromise, WsProvider} from '@polkadot/api'; import * as defs from '../../interfaces/definitions'; -import {TSigner} from './types'; import {IKeyringPair} from '@polkadot/types/types'; +export class SilentLogger { + log(_msg: any, _level: any): void { } + level = { + ERROR: 'ERROR' as const, + WARNING: 'WARNING' as const, + INFO: 'INFO' as const, + }; +} + +export class SilentConsole { + // TODO: Remove, this is temporary: Filter unneeded API output + // (Jaco promised it will be removed in the next version) + consoleErr: any; + consoleLog: any; + consoleWarn: any; + + constructor() { + this.consoleErr = console.error; + this.consoleLog = console.log; + this.consoleWarn = console.warn; + } + + enable() { + const outFn = (printer: any) => (...args: any[]) => { + for (const arg of args) { + if (typeof arg !== 'string') + continue; + if (arg.includes('1000:: Normal connection closure') || arg.includes('Not decorating unknown runtime apis:') || arg.includes('RPC methods not decorated:') || arg === 'Normal connection closure') + return; + } + printer(...args); + }; + + console.error = outFn(this.consoleErr.bind(console)); + console.log = outFn(this.consoleLog.bind(console)); + console.warn = outFn(this.consoleWarn.bind(console)); + } + + disable() { + console.error = this.consoleErr; + console.log = this.consoleLog; + console.warn = this.consoleWarn; + } +} + + export class DevUniqueHelper extends UniqueHelper { /** * Arrange methods for tests */ arrange: ArrangeGroup; + wait: WaitGroup; constructor(logger: { log: (msg: any, level: any) => void, level: any }) { super(logger); this.arrange = new ArrangeGroup(this); + this.wait = new WaitGroup(this); } - async connect(wsEndpoint: string, listeners?: any): Promise { + async connect(wsEndpoint: string, _listeners?: any): Promise { const wsProvider = new WsProvider(wsEndpoint); this.api = new ApiPromise({ provider: wsProvider, @@ -36,6 +83,7 @@ }, rpc: { unique: defs.unique.rpc, + appPromotion: defs.appPromotion.rpc, rmrk: defs.rmrk.rpc, eth: { feeHistory: { @@ -72,20 +120,21 @@ */ createAccounts = async (balances: bigint[], donor: IKeyringPair): Promise => { let nonce = await this.helper.chain.getNonce(donor.address); + const ss58Format = this.helper.chain.getChainProperties().ss58Format; const tokenNominal = this.helper.balance.getOneTokenNominal(); const transactions = []; const accounts: IKeyringPair[] = []; for (const balance of balances) { - const recepient = this.helper.util.fromSeed(mnemonicGenerate()); - accounts.push(recepient); + const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format); + accounts.push(recipient); if (balance !== 0n) { - const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recepient.address}, balance * tokenNominal]); + const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, balance * tokenNominal]); transactions.push(this.helper.signTransaction(donor, tx, 'account generation', {nonce})); nonce++; } } - await Promise.all(transactions).catch(e => {}); + await Promise.all(transactions).catch(_e => {}); //#region TODO remove this region, when nonce problem will be solved const checkBalances = async () => { @@ -105,7 +154,7 @@ for (let index = 0; index < 5; index++) { accountsCreated = await checkBalances(); if(accountsCreated) break; - await this.waitNewBlocks(1); + } if (!accountsCreated) throw Error('Accounts generation failed'); @@ -114,12 +163,86 @@ return accounts; }; + // TODO combine this method and createAccounts into one + createCrowd = async (accountsToCreate: number, withBalance: bigint, donor: IKeyringPair): Promise => { + const createAsManyAsCan = async () => { + let transactions: any = []; + const accounts: IKeyringPair[] = []; + let nonce = await this.helper.chain.getNonce(donor.address); + const tokenNominal = this.helper.balance.getOneTokenNominal(); + for (let i = 0; i < accountsToCreate; i++) { + if (i === 500) { // if there are too many accounts to create + await Promise.allSettled(transactions); // wait while first 500 (should be 100 for devnode) tx will be settled + transactions = []; // + nonce = await this.helper.chain.getNonce(donor.address); // update nonce + } + const recepient = this.helper.util.fromSeed(mnemonicGenerate()); + accounts.push(recepient); + if (withBalance !== 0n) { + const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recepient.address}, withBalance * tokenNominal]); + transactions.push(this.helper.signTransaction(donor, tx, 'account generation', {nonce})); + nonce++; + } + } + + const fullfilledAccounts = []; + await Promise.allSettled(transactions); + for (const account of accounts) { + const accountBalance = await this.helper.balance.getSubstrate(account.address); + if (accountBalance === withBalance * tokenNominal) { + fullfilledAccounts.push(account); + } + } + return fullfilledAccounts; + }; + + + const crowd: IKeyringPair[] = []; + // do up to 5 retries + for (let index = 0; index < 5 && accountsToCreate !== 0; index++) { + const asManyAsCan = await createAsManyAsCan(); + crowd.push(...asManyAsCan); + accountsToCreate -= asManyAsCan.length; + } + + if (accountsToCreate !== 0) throw Error(`Crowd generation failed: ${accountsToCreate} accounts left`); + + return crowd; + }; + + isDevNode = async () => { + const block1 = await this.helper.api?.rpc.chain.getBlock(await this.helper.api?.rpc.chain.getBlockHash(1)); + const block2 = await this.helper.api?.rpc.chain.getBlock(await this.helper.api?.rpc.chain.getBlockHash(2)); + const findCreationDate = async (block: any) => { + const humanBlock = block.toHuman(); + let date; + humanBlock.block.extrinsics.forEach((ext: any) => { + if(ext.method.section === 'timestamp') { + date = Number(ext.method.args.now.replaceAll(',', '')); + } + }); + return date; + }; + const block1date = await findCreationDate(block1); + const block2date = await findCreationDate(block2); + if(block2date! - block1date! < 9000) return true; + }; +} + +class WaitGroup { + helper: UniqueHelper; + + constructor(helper: UniqueHelper) { + this.helper = helper; + } + /** * Wait for specified bnumber of blocks * @param blocksCount number of blocks to wait * @returns */ - async waitNewBlocks(blocksCount = 1): Promise { + async newBlocks(blocksCount = 1): Promise { + // eslint-disable-next-line no-async-promise-executor const promise = new Promise(async (resolve) => { const unsubscribe = await this.helper.api!.rpc.chain.subscribeNewHeads(() => { if (blocksCount > 0) { @@ -132,4 +255,27 @@ }); return promise; } -} \ No newline at end of file + + async forParachainBlockNumber(blockNumber: bigint) { + return new Promise(async (resolve) => { + const unsubscribe = await this.helper.api!.rpc.chain.subscribeNewHeads(async (data: any) => { + if (data.number.toNumber() >= blockNumber) { + unsubscribe(); + resolve(); + } + }); + }); + } + + async forRelayBlockNumber(blockNumber: bigint) { + return new Promise(async (resolve) => { + const unsubscribe = await this.helper.api!.query.parachainSystem.validationData(async (data: any) => { + if (data.value.relayParentNumber.toNumber() >= blockNumber) { + // @ts-ignore + unsubscribe(); + resolve(); + } + }); + }); + } +} --- a/tests/src/util/playgrounds/unique.ts +++ b/tests/src/util/playgrounds/unique.ts @@ -9,15 +9,14 @@ import {ApiInterfaceEvents} from '@polkadot/api/types'; import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto'; import {IKeyringPair} from '@polkadot/types/types'; -import {IApiListeners, IChainEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types'; +import {IApiListeners, IBlock, IChainEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types'; -const crossAccountIdFromLower = (lowerAddress: ICrossAccountIdLower): ICrossAccountId => { +export const crossAccountIdFromLower = (lowerAddress: ICrossAccountIdLower): ICrossAccountId => { const address = {} as ICrossAccountId; if(lowerAddress.substrate) address.Substrate = lowerAddress.substrate; if(lowerAddress.ethereum) address.Ethereum = lowerAddress.ethereum; return address; }; - const nesting = { toChecksumAddress(address: string): string { @@ -91,9 +90,9 @@ return encodeAddress(decodeAddress(address), ss58Format); } - static extractCollectionIdFromCreationResult(creationResult: ITransactionResult, label = 'new collection') { + static extractCollectionIdFromCreationResult(creationResult: ITransactionResult) { if (creationResult.status !== this.transactionStatus.SUCCESS) { - throw Error(`Unable to create collection for ${label}`); + throw Error('Unable to create collection!'); } let collectionId = null; @@ -104,15 +103,15 @@ }); if (collectionId === null) { - throw Error(`No CollectionCreated event for ${label}`); + throw Error('No CollectionCreated event was found!'); } return collectionId; } - static extractTokensFromCreationResult(creationResult: ITransactionResult, label = 'new tokens') { + static extractTokensFromCreationResult(creationResult: ITransactionResult) { if (creationResult.status !== this.transactionStatus.SUCCESS) { - throw Error(`Unable to create tokens for ${label}`); + throw Error('Unable to create tokens!'); } let success = false; const tokens = [] as any; @@ -130,9 +129,9 @@ return {success, tokens}; } - static extractTokensFromBurnResult(burnResult: ITransactionResult, label = 'burned tokens') { + static extractTokensFromBurnResult(burnResult: ITransactionResult) { if (burnResult.status !== this.transactionStatus.SUCCESS) { - throw Error(`Unable to burn tokens for ${label}`); + throw Error('Unable to burn tokens!'); } let success = false; const tokens = [] as any; @@ -150,7 +149,7 @@ return {success, tokens}; } - static findCollectionInEvents(events: {event: IChainEvent}[], collectionId: number, expectedSection: string, expectedMethod: string, label?: string) { + static findCollectionInEvents(events: {event: IChainEvent}[], collectionId: number, expectedSection: string, expectedMethod: string) { let eventId = null; events.forEach(({event: {data, method, section}}) => { if ((section === expectedSection) && (method === expectedMethod)) { @@ -159,7 +158,7 @@ }); if (eventId === null) { - throw Error(`No ${expectedMethod} event for ${label}`); + throw Error(`No ${expectedMethod} event was found!`); } return eventId === collectionId; } @@ -317,6 +316,7 @@ if(options !== null) return transaction.signAndSend(sender, options, callback); return transaction.signAndSend(sender, callback); }; + // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { try { const unsub = await sign((result: any) => { @@ -364,7 +364,7 @@ return call(...params); } - async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=false, failureMessage='expected success') { + async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=false/*, failureMessage='expected success'*/) { if(this.api === null) throw Error('API not initialized'); if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`); @@ -388,6 +388,7 @@ type: this.chainLogType.EXTRINSIC, status: result.status, call: extrinsic, + signer: this.getSignerAddress(sender), params, } as IUniqueHelperLog; @@ -396,7 +397,7 @@ this.chainLog.push(log); - if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) throw Error(failureMessage); + if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) throw Error(`${result.moduleError}`); return result; } @@ -438,6 +439,16 @@ if(typeof signer === 'string') return signer; return signer.address; } + + fetchAllPalletNames(): string[] { + if(this.api === null) throw Error('API not initialized'); + return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase()); + } + + fetchMissingPalletNames(requiredPallets: string[]): string[] { + const palletNames = this.fetchAllPalletNames(); + return requiredPallets.filter(p => !palletNames.includes(p)); + } } @@ -474,7 +485,9 @@ } /** - * Get information about the collection with additional data, including the number of tokens it contains, its administrators, the normalized address of the collection's owner, and decoded name and description. + * Get information about the collection with additional data, + * including the number of tokens it contains, its administrators, + * the normalized address of the collection's owner, and decoded name and description. * * @param collectionId ID of collection * @example await getData(2) @@ -502,42 +515,50 @@ collectionData[key] = this.helper.util.vec2str(humanCollection[key]); } - collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode)) ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId) : 0; + collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode)) + ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId) + : 0; collectionData.admins = await this.getAdmins(collectionId); return collectionData; } /** - * Get the normalized addresses of the collection's administrators. + * Get the addresses of the collection's administrators, optionally normalized. * * @param collectionId ID of collection + * @param normalize whether to normalize the addresses to the default ss58 format * @example await getAdmins(1) * @returns array of administrators */ - async getAdmins(collectionId: number): Promise { - const normalized = []; - for(const admin of (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman()) { - if(admin.Substrate) normalized.push({Substrate: this.helper.address.normalizeSubstrate(admin.Substrate)}); - else normalized.push(admin); - } - return normalized; + async getAdmins(collectionId: number, normalize = false): Promise { + const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman(); + + return normalize + ? admins.map((address: any) => { + return address.Substrate + ? {Substrate: this.helper.address.normalizeSubstrate(address.Substrate)} + : address; + }) + : admins; } /** - * Get the normalized addresses added to the collection allow-list. + * Get the addresses added to the collection allow-list, optionally normalized. * @param collectionId ID of collection + * @param normalize whether to normalize the addresses to the default ss58 format * @example await getAllowList(1) * @returns array of allow-listed addresses */ - async getAllowList(collectionId: number): Promise { - const normalized = []; + async getAllowList(collectionId: number, normalize = false): Promise { const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman(); - for (const address of allowListed) { - if (address.Substrate) normalized.push({Substrate: this.helper.address.normalizeSubstrate(address.Substrate)}); - else normalized.push(address); - } - return normalized; + return normalize + ? allowListed.map((address: any) => { + return address.Substrate + ? {Substrate: this.helper.address.normalizeSubstrate(address.Substrate)} + : address; + }) + : allowListed; } /** @@ -556,40 +577,36 @@ * * @param signer keyring of signer * @param collectionId ID of collection - * @param label extra label for log * @example await helper.collection.burn(aliceKeyring, 3); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async burn(signer: TSigner, collectionId: number, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async burn(signer: TSigner, collectionId: number): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.destroyCollection', [collectionId], - true, `Unable to burn collection for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed'); } /** - * Sets the sponsor for the collection (Requires the Substrate address). + * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor. * * @param signer keyring of signer * @param collectionId ID of collection * @param sponsorAddress Sponsor substrate address - * @param label extra label for log * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...") * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress], - true, `Unable to set collection sponsor for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorSet', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorSet'); } /** @@ -597,19 +614,35 @@ * * @param signer keyring of signer * @param collectionId ID of collection - * @param label extra label for log * @example confirmSponsorship(aliceKeyring, 10) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async confirmSponsorship(signer: TSigner, collectionId: number, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async confirmSponsorship(signer: TSigner, collectionId: number): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.confirmSponsorship', [collectionId], - true, `Unable to confirm collection sponsorship for ${label}`, + true, + ); + + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'SponsorshipConfirmed'); + } + + /** + * Removes the sponsor of a collection, regardless if it consented or not. + * + * @param signer keyring of signer + * @param collectionId ID of collection + * @example removeSponsor(aliceKeyring, 10) + * @returns ```true``` if extrinsic success, otherwise ```false``` + */ + async removeSponsor(signer: TSigner, collectionId: number): Promise { + const result = await this.helper.executeExtrinsic( + signer, + 'api.tx.unique.removeCollectionSponsor', [collectionId], + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'SponsorshipConfirmed', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorRemoved'); } /** @@ -618,7 +651,6 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param limits collection limits object - * @param label extra label for log * @example * await setLimits( * aliceKeyring, @@ -630,15 +662,14 @@ * ) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.setCollectionLimits', [collectionId, limits], - true, `Unable to set collection limits for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionLimitSet', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionLimitSet'); } /** @@ -647,19 +678,17 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param ownerAddress substrate address of new owner - * @param label extra label for log * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...") * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress], - true, `Unable to change collection owner for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionOwnedChanged', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionOwnedChanged'); } /** @@ -668,80 +697,71 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param adminAddressObj Administrator address (substrate or ethereum) - * @param label extra label for log * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj], - true, `Unable to add collection admin for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminAdded', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminAdded'); } /** - * Adds an address to allow list + * Removes a collection administrator. + * * @param signer keyring of signer * @param collectionId ID of collection - * @param addressObj address to add to the allow list - * @param label extra label for log + * @param adminAddressObj Administrator address (substrate or ethereum) + * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise { const result = await this.helper.executeExtrinsic( signer, - 'api.tx.unique.addToAllowList', [collectionId, addressObj], - true, `Unable to add address to allow list for ${label}`, + 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj], + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressAdded'); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminRemoved'); } /** - * Removes an address from allow list. - * + * Adds an address to allow list * @param signer keyring of signer * @param collectionId ID of collection - * @param addressObj address to be removed from allow list (substrate or ethereum) - * @param label extra label for log - * @example removeFromAllowList(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}) + * @param addressObj address to add to the allow list * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise { const result = await this.helper.executeExtrinsic( signer, - 'api.tx.unique.removeFromAllowList', [collectionId, addressObj], - true, `Unable to remove address from allow list for ${label}`, + 'api.tx.unique.addToAllowList', [collectionId, addressObj], + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressRemoved', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressAdded'); } /** - * Removes a collection administrator. + * Removes an address from allow list * * @param signer keyring of signer * @param collectionId ID of collection - * @param adminAddressObj Administrator address (substrate or ethereum) - * @param label extra label for log - * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}) + * @param addressObj address to remove from the allow list * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise { const result = await this.helper.executeExtrinsic( signer, - 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj], - true, `Unable to remove collection admin for ${label}`, + 'api.tx.unique.removeFromAllowList', [collectionId, addressObj], + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminRemoved', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressRemoved'); } /** @@ -750,19 +770,17 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param permissions collection permissions object - * @param label extra label for log * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}}); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.setCollectionPermissions', [collectionId, permissions], - true, `Unable to set collection permissions for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionPermissionSet', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionPermissionSet'); } /** @@ -771,12 +789,11 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param permissions nesting permissions object - * @param label extra label for log * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true}); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions, label?: string): Promise { - return await this.setPermissions(signer, collectionId, {nesting: permissions}, label); + async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise { + return await this.setPermissions(signer, collectionId, {nesting: permissions}); } /** @@ -784,12 +801,11 @@ * * @param signer keyring of signer * @param collectionId ID of collection - * @param label extra label for log * @example disableNesting(aliceKeyring, 10); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async disableNesting(signer: TSigner, collectionId: number, label?: string): Promise { - return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}}, label); + async disableNesting(signer: TSigner, collectionId: number): Promise { + return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}}); } /** @@ -798,19 +814,17 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param properties array of property objects - * @param label extra label for log * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async setProperties(signer: TSigner, collectionId: number, properties: IProperty[], label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.setCollectionProperties', [collectionId, properties], - true, `Unable to set collection properties for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet'); } /** @@ -819,19 +833,17 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param propertyKeys array of property keys to delete - * @param label * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[], label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys], - true, `Unable to delete collection properties for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted'); } /** @@ -849,7 +861,7 @@ const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount], - true, `Unable to transfer token #${tokenId} from collection #${collectionId}`, + true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`, ); return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount); @@ -872,7 +884,7 @@ const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount], - true, `Unable to transfer token #${tokenId} from collection #${collectionId}`, + true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`, ); return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount); } @@ -884,22 +896,20 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param tokenId ID of token - * @param label * @param amount amount of tokens to be burned. For NFT must be set to 1n * @example burnToken(aliceKeyring, 10, 5); * @returns ```true``` and burnt token number is extrinsic success. Otherwise ```false``` and ```null``` */ - async burnToken(signer: TSigner, collectionId: number, tokenId: number, label?: string, amount=1n): Promise<{ + async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<{ success: boolean, token: number | null }> { - if(typeof label === 'undefined') label = `collection #${collectionId}`; const burnResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.burnItem', [collectionId, tokenId, amount], - true, `Unable to burn token for ${label}`, + true, // `Unable to burn token for ${label}`, ); - const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult, label); + const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult); if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens'); return {success: burnedTokens.success, token: burnedTokens.tokens.length > 0 ? burnedTokens.tokens[0] : null}; } @@ -911,19 +921,17 @@ * @param collectionId ID of collection * @param fromAddressObj address on behalf of which the token will be burnt * @param tokenId ID of token - * @param label * @param amount amount of tokens to be burned. For NFT must be set to 1n * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."}) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async burnTokenFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, tokenId: number, label?: string, amount=1n): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async burnTokenFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, tokenId: number, amount=1n): Promise { const burnResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount], - true, `Unable to burn token from for ${label}`, + true, // `Unable to burn token from for ${label}`, ); - const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult, label); + const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult); return burnedTokens.success && burnedTokens.tokens.length > 0; } @@ -933,28 +941,27 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param tokenId ID of token - * @param toAddressObj - * @param label + * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens * @param amount amount of token to be approved. For NFT must be set to 1n * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, label?: string, amount=1n) { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) { const approveResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount], - true, `Unable to approve token for ${label}`, + true, // `Unable to approve token for ${label}`, ); - return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved', label); + return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved'); } /** - * Get the amount of token pieces approved to transfer + * Get the amount of token pieces approved to transfer or burn. Normally 0. + * * @param collectionId ID of collection * @param tokenId ID of token - * @param toAccountObj - * @param fromAccountObj + * @param toAccountObj address which is approved to use token pieces + * @param fromAccountObj address which may have allowed the use of its owned tokens * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."}) * @returns number of approved to transfer pieces */ @@ -963,7 +970,8 @@ } /** - * Get the last created token id + * Get the last created token ID in a collection + * * @param collectionId ID of collection * @example getLastTokenId(10); * @returns id of the last created token @@ -974,6 +982,7 @@ /** * Check if token exists + * * @param collectionId ID of collection * @param tokenId ID of token * @example isTokenExists(10, 20); @@ -999,14 +1008,15 @@ /** * Get token data + * * @param collectionId ID of collection * @param tokenId ID of token - * @param blockHashAt - * @param propertyKeys + * @param propertyKeys optionally filter the token properties to only these keys + * @param blockHashAt optionally query the data at some block with this hash * @example getToken(10, 5); * @returns human readable token data */ - async getToken(collectionId: number, tokenId: number, blockHashAt?: string, propertyKeys?: string[]): Promise<{ + async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{ properties: IProperty[]; owner: ICrossAccountId; normalizedOwner: ICrossAccountId; @@ -1016,7 +1026,7 @@ tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]); } else { - if(typeof propertyKeys === 'undefined') { + if(propertyKeys.length == 0) { const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman(); if(!collection) return null; propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key); @@ -1035,45 +1045,43 @@ /** * Set permissions to change token properties + * * @param signer keyring of signer * @param collectionId ID of collection * @param permissions permissions to change a property by the collection owner or admin - * @param label * @example setTokenPropertyPermissions( * aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}] * ) * @returns true if extrinsic success otherwise false */ - async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[], label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions], - true, `Unable to set token property permissions for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet'); } /** * Set token properties + * * @param signer keyring of signer * @param collectionId ID of collection * @param tokenId ID of token - * @param properties - * @param label + * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}]) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[], label?: string): Promise { - if(typeof label === 'undefined') label = `token #${tokenId} from collection #${collectionId}`; + async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties], - true, `Unable to set token properties for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet'); } /** @@ -1082,31 +1090,29 @@ * @param collectionId ID of collection * @param tokenId ID of token * @param propertyKeys property keys to be deleted - * @param label * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"]) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[], label?: string): Promise { - if(typeof label === 'undefined') label = `token #${tokenId} from collection #${collectionId}`; + async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise { const result = await this.helper.executeExtrinsic( signer, 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys], - true, `Unable to delete token properties for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted', label); + return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted'); } /** * Mint new collection + * * @param signer keyring of signer * @param collectionOptions basic collection options and properties * @param mode NFT or RFT type of a collection - * @param errorLabel * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT") * @returns object of the created collection */ - async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT', errorLabel = 'Unable to mint collection'): Promise { + async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise { collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null}; for (const key of ['name', 'description', 'tokenPrefix']) { @@ -1115,16 +1121,16 @@ const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createCollectionEx', [collectionOptions], - true, errorLabel, + true, // errorLabel, ); - return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult, errorLabel)); + return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult)); } - getCollectionObject(collectionId: number): any { + getCollectionObject(_collectionId: number): any { return null; } - getTokenObject(collectionId: number, tokenId: number): any { + getTokenObject(_collectionId: number, _tokenId: number): any { return null; } } @@ -1156,7 +1162,7 @@ * Get token's owner * @param collectionId ID of collection * @param tokenId ID of token - * @param blockHashAt + * @param blockHashAt optionally query the data at the block with this hash * @example getTokenOwner(10, 5); * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."} */ @@ -1238,7 +1244,7 @@ * Get tokens nested in the provided token * @param collectionId ID of collection * @param tokenId ID of token - * @param blockHashAt + * @param blockHashAt optionally query the data at the block with this hash * @example getTokenChildren(10, 5); * @returns tokens whose depth of nesting is <= 5 */ @@ -1260,15 +1266,14 @@ * @param signer keyring of signer * @param tokenObj token to be nested * @param rootTokenObj token to be parent - * @param label * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, label='nest token'): Promise { + async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise { const rootTokenAddress = {Ethereum: this.helper.util.getNestingTokenAddress(rootTokenObj.collectionId, rootTokenObj.tokenId)}; const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress); if(!result) { - throw Error(`Unable to nest token for ${label}`); + throw Error('Unable to nest token!'); } return result; } @@ -1279,15 +1284,14 @@ * @param tokenObj token to unnest * @param rootTokenObj parent of a token * @param toAddressObj address of a new token owner - * @param label * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."}); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId, label='unnest token'): Promise { + async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise { const rootTokenAddress = {Ethereum: this.helper.util.getNestingTokenAddress(rootTokenObj.collectionId, rootTokenObj.tokenId)}; const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj); if(!result) { - throw Error(`Unable to unnest token for ${label}`); + throw Error('Unable to unnest token!'); } return result; } @@ -1296,7 +1300,6 @@ * Mint new collection * @param signer keyring of signer * @param collectionOptions Collection options - * @param label * @example * mintCollection(aliceKeyring, { * name: 'New', @@ -1305,19 +1308,17 @@ * }) * @returns object of the created collection */ - async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, label = 'new collection'): Promise { - return await super.mintCollection(signer, collectionOptions, 'NFT', `Unable to mint NFT collection for ${label}`) as UniqueNFTCollection; + async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions): Promise { + return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection; } /** * Mint new token * @param signer keyring of signer * @param data token data - * @param label * @returns created token object */ - async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${data.collectionId}`; + async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise { const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, { @@ -1325,9 +1326,9 @@ properties: data.properties, }, }], - true, `Unable to mint NFT token for ${label}`, + true, ); - const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult, label); + const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult); if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens'); if (createdTokens.tokens.length < 1) throw Error('No tokens minted'); return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId); @@ -1338,7 +1339,6 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param tokens array of tokens with owner and properties - * @param label * @example * mintMultipleTokens(aliceKeyring, 10, [{ * owner: {Substrate: "5DyN4Y92vZCjv38fg..."}, @@ -1349,15 +1349,14 @@ * }]); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[], label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise { const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}], - true, `Unable to mint NFT tokens for ${label}`, + true, ); const collection = this.getCollectionObject(collectionId); - return this.helper.util.extractTokensFromCreationResult(creationResult, label).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); + return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); } /** @@ -1366,7 +1365,6 @@ * @param collectionId ID of collection * @param owner tokens owner * @param tokens array of tokens with owner and properties - * @param label * @example * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{ * properties: [{ @@ -1379,8 +1377,7 @@ * }]); * @returns array of newly created tokens */ - async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[], label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise { const rawTokens = []; for (const token of tokens) { const raw = {NFT: {properties: token.properties}}; @@ -1389,23 +1386,10 @@ const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens], - true, `Unable to mint NFT tokens for ${label}`, + true, ); const collection = this.getCollectionObject(collectionId); - return this.helper.util.extractTokensFromCreationResult(creationResult, label).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); - } - - /** - * Destroys a concrete instance of NFT. - * @param signer keyring of signer - * @param collectionId ID of collection - * @param tokenId ID of token - * @param label - * @example burnToken(aliceKeyring, 10, 5); - * @returns ```true``` and burnt token number is extrinsic success. Otherwise ```false``` and ```null``` - */ - async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, label?: string): Promise<{ success: boolean; token: number | null; }> { - return await super.burnToken(signer, collectionId, tokenId, label, 1n); + return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); } /** @@ -1415,12 +1399,11 @@ * @param collectionId ID of collection * @param tokenId ID of token * @param toAddressObj address to approve - * @param label * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, label?: string, amount=1n) { - return super.approveToken(signer, collectionId, tokenId, toAddressObj, label, amount); + async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) { + return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n); } } @@ -1480,7 +1463,7 @@ * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=100n): Promise { + async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise { return await super.transferToken(signer, collectionId, tokenId, addressObj, amount); } @@ -1495,7 +1478,7 @@ * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=100n): Promise { + async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise { return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount); } @@ -1503,7 +1486,6 @@ * Mint new collection * @param signer keyring of signer * @param collectionOptions Collection options - * @param label * @example * mintCollection(aliceKeyring, { * name: 'New', @@ -1512,20 +1494,18 @@ * }) * @returns object of the created collection */ - async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, label = 'new collection'): Promise { - return await super.mintCollection(signer, collectionOptions, 'RFT', `Unable to mint RFT collection for ${label}`) as UniqueRFTCollection; + async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions): Promise { + return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection; } /** * Mint new token * @param signer keyring of signer * @param data token data - * @param label * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n}); * @returns created token object */ - async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${data.collectionId}`; + async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise { const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, { @@ -1534,24 +1514,23 @@ properties: data.properties, }, }], - true, `Unable to mint RFT token for ${label}`, + true, ); - const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult, label); + const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult); if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens'); if (createdTokens.tokens.length < 1) throw Error('No tokens minted'); return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId); } - async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[], label?: string): Promise { + async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise { throw Error('Not implemented'); - if(typeof label === 'undefined') label = `collection #${collectionId}`; const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}], - true, `Unable to mint RFT tokens for ${label}`, + true, // `Unable to mint RFT tokens for ${label}`, ); const collection = this.getCollectionObject(collectionId); - return this.helper.util.extractTokensFromCreationResult(creationResult, label).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); + return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); } /** @@ -1560,12 +1539,10 @@ * @param collectionId ID of collection * @param owner tokens owner * @param tokens array of tokens with properties and pieces - * @param label * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]); * @returns array of newly created RFT tokens */ - async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[], label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise { const rawTokens = []; for (const token of tokens) { const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}}; @@ -1574,10 +1551,10 @@ const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens], - true, `Unable to mint RFT tokens for ${label}`, + true, ); const collection = this.getCollectionObject(collectionId); - return this.helper.util.extractTokensFromCreationResult(creationResult, label).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); + return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId)); } /** @@ -1585,13 +1562,12 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param tokenId ID of token - * @param label * @param amount number of pieces to be burnt * @example burnToken(aliceKeyring, 10, 5); * @returns ```true``` and burnt token number is extrinsic success. Otherwise ```false``` and ```null``` */ - async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, label?: string, amount=100n): Promise<{ success: boolean; token: number | null; }> { - return await super.burnToken(signer, collectionId, tokenId, label, amount); + async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<{ success: boolean; token: number | null; }> { + return await super.burnToken(signer, collectionId, tokenId, amount); } /** @@ -1601,13 +1577,12 @@ * @param collectionId ID of collection * @param tokenId ID of token * @param toAddressObj address to approve - * @param label * @param amount number of pieces to be approved * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n); * @returns true if the token success, otherwise false */ - async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, label?: string, amount=100n) { - return super.approveToken(signer, collectionId, tokenId, toAddressObj, label, amount); + async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) { + return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount); } /** @@ -1627,20 +1602,18 @@ * @param collectionId ID of collection * @param tokenId ID of token * @param amount new number of pieces - * @param label * @example repartitionToken(aliceKeyring, 10, 5, 12345n); * @returns true if the repartion was success, otherwise false */ - async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise { const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId); const repartitionResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.repartition', [collectionId, tokenId, amount], - true, `Unable to repartition RFT token for ${label}`, + true, ); - if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated', label); - return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed', label); + if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated'); + return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed'); } } @@ -1661,7 +1634,6 @@ * @param signer keyring of signer * @param collectionOptions Collection options * @param decimalPoints number of token decimals - * @param errorLabel * @example * mintCollection(aliceKeyring, { * name: 'New', @@ -1670,7 +1642,7 @@ * }, 18) * @returns newly created fungible collection */ - async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, decimalPoints = 0, errorLabel = 'Unable to mint collection'): Promise { + async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, decimalPoints = 0): Promise { collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions'); collectionOptions.mode = {fungible: decimalPoints}; @@ -1680,9 +1652,9 @@ const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createCollectionEx', [collectionOptions], - true, errorLabel, + true, ); - return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult, errorLabel)); + return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult)); } /** @@ -1691,12 +1663,10 @@ * @param collectionId ID of collection * @param owner address owner of new tokens * @param amount amount of tokens to be meanted - * @param label * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async mintTokens(signer: TSigner, collectionId: number, owner: ICrossAccountId | string, amount: bigint, label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async mintTokens(signer: TSigner, collectionId: number, owner: ICrossAccountId | string, amount: bigint): Promise { const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, { @@ -1704,9 +1674,9 @@ value: amount, }, }], - true, `Unable to mint fungible tokens for ${label}`, + true, // `Unable to mint fungible tokens for ${label}`, ); - return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated', label); + return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated'); } /** @@ -1715,11 +1685,9 @@ * @param collectionId ID of collection * @param owner tokens owner * @param tokens array of tokens with properties and pieces - * @param label * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {value: bigint}[], label?: string): Promise { - if(typeof label === 'undefined') label = `collection #${collectionId}`; + async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {value: bigint}[]): Promise { const rawTokens = []; for (const token of tokens) { const raw = {Fungible: {Value: token.value}}; @@ -1728,9 +1696,9 @@ const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens], - true, `Unable to mint RFT tokens for ${label}`, + true, ); - return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated', label); + return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated'); } /** @@ -1758,12 +1726,12 @@ * Transfer tokens to address * @param signer keyring of signer * @param collectionId ID of collection - * @param toAddressObj address recepient + * @param toAddressObj address recipient * @param amount amount of tokens to be sent * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount: bigint) { + async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) { return await super.transferToken(signer, collectionId, 0, toAddressObj, amount); } @@ -1777,7 +1745,7 @@ * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount: bigint) { + async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) { return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount); } @@ -1786,12 +1754,11 @@ * @param signer keyring of signer * @param collectionId ID of collection * @param amount amount of tokens to be destroyed - * @param label * @example burnTokens(aliceKeyring, 10, 1000n); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async burnTokens(signer: IKeyringPair, collectionId: number, amount=100n, label?: string): Promise { - return (await super.burnToken(signer, collectionId, 0, label, amount)).success; + async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise { + return (await super.burnToken(signer, collectionId, 0, amount)).success; } /** @@ -1800,12 +1767,11 @@ * @param collectionId ID of collection * @param fromAddressObj address on behalf of which tokens will be burnt * @param amount amount of tokens to be burnt - * @param label * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=100n, label?: string): Promise { - return await super.burnTokenFrom(signer, collectionId, fromAddressObj, 0, label, amount); + async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise { + return await super.burnTokenFrom(signer, collectionId, fromAddressObj, 0, amount); } /** @@ -1824,12 +1790,11 @@ * @param collectionId ID of collection * @param toAddressObj address to be approved * @param amount amount of tokens to be approved - * @param label * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n) * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=100n, label?: string) { - return super.approveToken(signer, collectionId, 0, toAddressObj, label, amount); + async approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) { + return super.approveToken(signer, collectionId, 0, toAddressObj, amount); } /** @@ -1881,6 +1846,13 @@ return blockHash; } + // TODO add docs + async getBlock(blockHashOrNumber: string | number): Promise { + const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber); + if (!blockHash) return null; + return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block; + } + /** * Get account nonce * @param address substrate address @@ -1915,6 +1887,16 @@ } /** + * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved + * @param address substrate address + * @returns + */ + async getSubstrateFull(address: TSubstrateAccount): Promise { + const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data; + return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()}; + } + + /** * Get ethereum address balance * @param address ethereum address * @example getEthereum("0x9F0583DbB855d...") @@ -1927,13 +1909,13 @@ /** * Transfer tokens to substrate address * @param signer keyring of signer - * @param address substrate address of a recepient + * @param address substrate address of a recipient * @param amount amount of tokens to be transfered * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n); * @returns ```true``` if extrinsic success, otherwise ```false``` */ async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise { - const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`); + const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/); let transfer = {from: null, to: null, amount: 0n} as any; result.result.events.forEach(({event: {data, method, section}}) => { @@ -2000,7 +1982,59 @@ } } +class StakingGroup extends HelperGroup { + /** + * Stake tokens for App Promotion + * @param signer keyring of signer + * @param amountToStake amount of tokens to stake + * @param label extra label for log + * @returns + */ + async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise { + if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`; + const stakeResult = await this.helper.executeExtrinsic( + signer, 'api.tx.appPromotion.stake', + [amountToStake], true, + ); + // TODO extract info from stakeResult + return true; + } + /** + * Unstake tokens for App Promotion + * @param signer keyring of signer + * @param amountToUnstake amount of tokens to unstake + * @param label extra label for log + * @returns block number where balances will be unlocked + */ + async unstake(signer: TSigner, label?: string): Promise { + if(typeof label === 'undefined') label = `${signer.address}`; + const unstakeResult = await this.helper.executeExtrinsic( + signer, 'api.tx.appPromotion.unstake', + [], true, + ); + // TODO extract block number fron events + return 1; + } + + async getTotalStaked(address?: ICrossAccountId): Promise { + if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt(); + return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt(); + } + + async getTotalStakedPerBlock(address: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]); + } + + async getPendingUnstake(address: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt(); + } + + async getPendingUnstakePerBlock(address: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]); + } +} + export class UniqueHelper extends ChainHelperBase { chain: ChainGroup; balance: BalanceGroup; @@ -2009,6 +2043,7 @@ nft: NFTGroup; rft: RFTGroup; ft: FTGroup; + staking: StakingGroup; constructor(logger?: ILogger) { super(logger); @@ -2019,6 +2054,7 @@ this.nft = new NFTGroup(this); this.rft = new RFTGroup(this); this.ft = new FTGroup(this); + this.staking = new StakingGroup(this); } } @@ -2056,64 +2092,72 @@ return await this.helper.collection.getEffectiveLimits(this.collectionId); } - async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount, label?: string) { - return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress, label); + async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) { + return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress); } - async confirmSponsorship(signer: TSigner, label?: string) { - return await this.helper.collection.confirmSponsorship(signer, this.collectionId, label); + async confirmSponsorship(signer: TSigner) { + return await this.helper.collection.confirmSponsorship(signer, this.collectionId); } - async setLimits(signer: TSigner, limits: ICollectionLimits, label?: string) { - return await this.helper.collection.setLimits(signer, this.collectionId, limits, label); + async removeSponsor(signer: TSigner) { + return await this.helper.collection.removeSponsor(signer, this.collectionId); } - async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount, label?: string) { - return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress, label); + async setLimits(signer: TSigner, limits: ICollectionLimits) { + return await this.helper.collection.setLimits(signer, this.collectionId, limits); + } + + async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) { + return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress); + } + + async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) { + return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj); } - async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId, label?: string) { - return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj, label); + async enableCertainPermissions(signer: TSigner, accessMode: 'AllowList' | 'Normal' | undefined = 'AllowList', mintMode: boolean | undefined = true) { + return await this.setPermissions(signer, {access: accessMode, mintMode: mintMode}); } - async addToAllowList(signer: TSigner, addressObj: ICrossAccountId, label?: string) { - return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj, label); + async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) { + return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj); } - async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId, label?: string) { - return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj, label); + async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) { + return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj); } - async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId, label?: string) { - return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj, label); + async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) { + return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj); } - async setProperties(signer: TSigner, properties: IProperty[], label?: string) { - return await this.helper.collection.setProperties(signer, this.collectionId, properties, label); + async setProperties(signer: TSigner, properties: IProperty[]) { + return await this.helper.collection.setProperties(signer, this.collectionId, properties); } - async deleteProperties(signer: TSigner, propertyKeys: string[], label?: string) { - return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys, label); + async deleteProperties(signer: TSigner, propertyKeys: string[]) { + return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys); } async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) { return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj); } - async setPermissions(signer: TSigner, permissions: ICollectionPermissions, label?: string) { - return await this.helper.collection.setPermissions(signer, this.collectionId, permissions, label); + async setPermissions(signer: TSigner, permissions: ICollectionPermissions) { + return await this.helper.collection.setPermissions(signer, this.collectionId, permissions); } - async enableNesting(signer: TSigner, permissions: INestingPermissions, label?: string) { - return await this.helper.collection.enableNesting(signer, this.collectionId, permissions, label); + async enableNesting(signer: TSigner, permissions: INestingPermissions) { + return await this.helper.collection.enableNesting(signer, this.collectionId, permissions); } - async disableNesting(signer: TSigner, label?: string) { - return await this.helper.collection.disableNesting(signer, this.collectionId, label); + async disableNesting(signer: TSigner) { + return await this.helper.collection.disableNesting(signer, this.collectionId); } - async burn(signer: TSigner, label?: string) { - return await this.helper.collection.burn(signer, this.collectionId, label); + async burn(signer: TSigner) { + return await this.helper.collection.burn(signer, this.collectionId); } } @@ -2128,7 +2172,7 @@ } async getToken(tokenId: number, blockHashAt?: string) { - return await this.helper.nft.getToken(this.collectionId, tokenId, blockHashAt); + return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt); } async getTokenOwner(tokenId: number, blockHashAt?: string) { @@ -2151,44 +2195,44 @@ return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj); } - async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, label?: string) { - return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj, label); + async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) { + return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj); } async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) { return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj); } - async mintToken(signer: TSigner, owner: ICrossAccountId, properties?: IProperty[], label?: string) { - return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties}, label); + async mintToken(signer: TSigner, owner: ICrossAccountId, properties?: IProperty[]) { + return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties}); } - async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[], label?: string) { - return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens, label); + async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) { + return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens); } - async burnToken(signer: TSigner, tokenId: number, label?: string) { - return await this.helper.nft.burnToken(signer, this.collectionId, tokenId, label); + async burnToken(signer: TSigner, tokenId: number) { + return await this.helper.nft.burnToken(signer, this.collectionId, tokenId); } - async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[], label?: string) { - return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties, label); + async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) { + return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties); } - async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[], label?: string) { - return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys, label); + async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) { + return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys); } - async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[], label?: string) { - return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions, label); + async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) { + return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions); } - async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken, label?: string) { - return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj, label); + async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) { + return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj); } - async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId, label?: string) { - return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj, label); + async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) { + return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj); } } @@ -2214,59 +2258,59 @@ return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId); } - async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=100n) { + async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) { return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount); } - async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=100n) { + async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) { return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount); } - async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=100n, label?: string) { - return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, label, amount); + async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) { + return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount); } async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj); } - async repartitionToken(signer: TSigner, tokenId: number, amount: bigint, label?: string) { - return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount, label); + async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) { + return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount); } - async mintToken(signer: TSigner, owner: ICrossAccountId, pieces=100n, properties?: IProperty[], label?: string) { - return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties}, label); + async mintToken(signer: TSigner, owner: ICrossAccountId, pieces=100n, properties?: IProperty[]) { + return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties}); } - async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[], label?: string) { - return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens, label); + async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]) { + return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens); } - async burnToken(signer: TSigner, tokenId: number, amount=100n, label?: string) { - return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, label, amount); + async burnToken(signer: TSigner, tokenId: number, amount=1n) { + return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount); } - async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[], label?: string) { - return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties, label); + async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) { + return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties); } - async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[], label?: string) { - return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys, label); + async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) { + return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys); } - async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[], label?: string) { - return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions, label); + async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) { + return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions); } } class UniqueFTCollection extends UniqueCollectionBase { - async mint(signer: TSigner, owner: ICrossAccountId, amount: bigint, label?: string) { - return await this.helper.ft.mintTokens(signer, this.collectionId, owner, amount, label); + async mint(signer: TSigner, owner: ICrossAccountId, amount: bigint) { + return await this.helper.ft.mintTokens(signer, this.collectionId, owner, amount); } - async mintWithOneOwner(signer: TSigner, owner: ICrossAccountId, tokens: {value: bigint}[], label?: string) { - return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, owner, tokens, label); + async mintWithOneOwner(signer: TSigner, owner: ICrossAccountId, tokens: {value: bigint}[]) { + return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, owner, tokens); } async getBalance(addressObj: ICrossAccountId) { @@ -2277,28 +2321,28 @@ return await this.helper.ft.getTop10Owners(this.collectionId); } - async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount: bigint) { + async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) { return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount); } - async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount: bigint) { + async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) { return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount); } - async burnTokens(signer: TSigner, amount: bigint, label?: string) { - return await this.helper.ft.burnTokens(signer, this.collectionId, amount, label); + async burnTokens(signer: TSigner, amount=1n) { + return await this.helper.ft.burnTokens(signer, this.collectionId, amount); } - async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount: bigint, label?: string) { - return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount, label); + async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) { + return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount); } async getTotalPieces() { return await this.helper.ft.getTotalPieces(this.collectionId); } - async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=100n, label?: string) { - return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount, label); + async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) { + return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount); } async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) { @@ -2322,12 +2366,12 @@ return await this.collection.getTokenNextSponsored(this.tokenId, addressObj); } - async setProperties(signer: TSigner, properties: IProperty[], label?: string) { - return await this.collection.setTokenProperties(signer, this.tokenId, properties, label); + async setProperties(signer: TSigner, properties: IProperty[]) { + return await this.collection.setTokenProperties(signer, this.tokenId, properties); } - async deleteProperties(signer: TSigner, propertyKeys: string[], label?: string) { - return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys, label); + async deleteProperties(signer: TSigner, propertyKeys: string[]) { + return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys); } } @@ -2356,12 +2400,12 @@ return await this.collection.getTokenChildren(this.tokenId, blockHashAt); } - async nest(signer: TSigner, toTokenObj: IToken, label?: string) { - return await this.collection.nestToken(signer, this.tokenId, toTokenObj, label); + async nest(signer: TSigner, toTokenObj: IToken) { + return await this.collection.nestToken(signer, this.tokenId, toTokenObj); } - async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId, label?: string) { - return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj, label); + async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) { + return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj); } async transfer(signer: TSigner, addressObj: ICrossAccountId) { @@ -2372,16 +2416,16 @@ return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj); } - async approve(signer: TSigner, toAddressObj: ICrossAccountId, label?: string) { - return await this.collection.approveToken(signer, this.tokenId, toAddressObj, label); + async approve(signer: TSigner, toAddressObj: ICrossAccountId) { + return await this.collection.approveToken(signer, this.tokenId, toAddressObj); } async isApproved(toAddressObj: ICrossAccountId) { return await this.collection.isTokenApproved(this.tokenId, toAddressObj); } - async burn(signer: TSigner, label?: string) { - return await this.collection.burnToken(signer, this.tokenId, label); + async burn(signer: TSigner) { + return await this.collection.burnToken(signer, this.tokenId); } } @@ -2405,27 +2449,27 @@ return await this.collection.getTokenTotalPieces(this.tokenId); } - async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=100n) { + async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) { return await this.collection.transferToken(signer, this.tokenId, addressObj, amount); } - async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=100n) { + async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) { return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount); } - async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=100n, label?: string) { - return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount, label); + async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) { + return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount); } async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) { return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj); } - async repartition(signer: TSigner, amount: bigint, label?: string) { - return await this.collection.repartitionToken(signer, this.tokenId, amount, label); + async repartition(signer: TSigner, amount: bigint) { + return await this.collection.repartitionToken(signer, this.tokenId, amount); } - async burn(signer: TSigner, amount=100n, label?: string) { - return await this.collection.burnToken(signer, this.tokenId, amount, label); + async burn(signer: TSigner, amount=1n) { + return await this.collection.burnToken(signer, this.tokenId, amount); } }