diff --git a/Cargo.lock b/Cargo.lock index cf98f385b..ea1e0ce01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1420,6 +1420,7 @@ dependencies = [ "bifrost-salp", "bifrost-salp-rpc-runtime-api", "bifrost-slp", + "bifrost-slp-v2", "bifrost-slpx", "bifrost-stable-asset", "bifrost-stable-pool", @@ -1560,6 +1561,7 @@ dependencies = [ "bstringify", "frame-support", "orml-oracle", + "orml-traits", "parity-scale-codec", "scale-info", "serde", @@ -1892,6 +1894,36 @@ dependencies = [ "staging-xcm-executor", ] +[[package]] +name = "bifrost-slp-v2" +version = "0.0.0" +dependencies = [ + "bifrost-asset-registry", + "bifrost-currencies", + "bifrost-primitives", + "bifrost-vtoken-minting", + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal 0.4.1", + "orml-tokens", + "orml-traits", + "pallet-balances", + "pallet-utility", + "pallet-xcm", + "parity-scale-codec", + "polkadot-parachain-primitives", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.13.0)", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + [[package]] name = "bifrost-slpx" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 8dfb87d0f..2af77d9c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "pallets/evm-accounts", "pallets/xcm-interface", "pallets/slp", + "pallets/slp-v2", "pallets/vstoken-conversion", "pallets/vtoken-minting", "pallets/vtoken-voting", @@ -78,6 +79,7 @@ bifrost-salp = { path = "pallets/salp", default-featur bifrost-salp-rpc-runtime-api = { path = "pallets/salp/rpc/runtime-api", default-features = false } bifrost-service = { path = "node/service", default-features = false } bifrost-slp = { path = "pallets/slp", default-features = false } +bifrost-slp-v2 = { path = "pallets/slp-v2", default-features = false } bifrost-slpx = { path = "pallets/slpx", default-features = false } bifrost-stable-asset = { path = "pallets/stable-asset", default-features = false } bifrost-stable-pool = { path = "pallets/stable-pool", default-features = false } diff --git a/pallets/slp-v2/Cargo.toml b/pallets/slp-v2/Cargo.toml new file mode 100644 index 000000000..d1879f580 --- /dev/null +++ b/pallets/slp-v2/Cargo.toml @@ -0,0 +1,78 @@ +[package] +name = "bifrost-slp-v2" +authors = ["Liebi Technologies "] +edition = "2021" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true, features = ["derive"] } +scale-info = { workspace = true, features = ["derive"] } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +bifrost-primitives = { workspace = true } +xcm = { workspace = true } +pallet-xcm = { workspace = true } +pallet-balances = { workspace = true } +xcm-executor = { workspace = true } +xcm-builder = { workspace = true } +orml-traits = { workspace = true } +polkadot-parachain-primitives = { workspace = true } +sp-core = { workspace = true } +sp-std = { workspace = true } +sp-runtime = { workspace = true } +cumulus-primitives-core = { workspace = true } +bifrost-asset-registry = { workspace = true } + +[dev-dependencies] +hex-literal = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +orml-tokens = { workspace = true } +pallet-utility = { workspace = true } +bifrost-vtoken-minting = { workspace = true } +bifrost-currencies = { workspace = true } + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", +] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-std/std", + "sp-runtime/std", + "bifrost-primitives/std", + "xcm/std", + "xcm-executor/std", + "xcm-builder/std", + "orml-traits/std", + "polkadot-parachain-primitives/std", + "sp-core/std", + "sp-runtime/std", + "pallet-xcm/std", + "cumulus-primitives-core/std", + "pallet-balances/std", + "bifrost-asset-registry/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] +kusama = [] +polkadot = [] diff --git a/pallets/slp-v2/src/astar_dapp_staking/impls.rs b/pallets/slp-v2/src/astar_dapp_staking/impls.rs new file mode 100644 index 000000000..16ba47986 --- /dev/null +++ b/pallets/slp-v2/src/astar_dapp_staking/impls.rs @@ -0,0 +1,243 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate::{ + astar_dapp_staking::types::{ + AstarCall, AstarDappStakingPendingStatus, AstarUnlockingRecord, AstarValidator, DappStaking, + }, + common::types::{ + Delegator, DelegatorIndex, Ledger, PendingStatus, StakingProtocol, Validator, XcmTask, + }, + Call, Config, ConfigurationByStakingProtocol, DelegatorByStakingProtocolAndDelegatorIndex, + DelegatorIndexByStakingProtocolAndDelegator, Error, Event, LedgerByStakingProtocolAndDelegator, + Pallet, PendingStatusByQueryId, ValidatorsByStakingProtocolAndDelegator, +}; +use bifrost_primitives::VtokenMintingOperator; +use frame_support::{dispatch::DispatchResultWithPostInfo, ensure}; +use parity_scale_codec::Encode; +use sp_std::{cmp::Ordering, vec::Vec}; +use xcm::v4::{opaque::Xcm, Location, QueryId}; + +pub const ASTAR_DAPP_STAKING: StakingProtocol = StakingProtocol::AstarDappStaking; + +impl Pallet { + pub fn ensure_validator_exist( + delegator: Delegator, + validator: AstarValidator, + ) -> DispatchResultWithPostInfo { + let validators = + ValidatorsByStakingProtocolAndDelegator::::get(ASTAR_DAPP_STAKING, delegator); + let is_exist = validators.iter().any(|storage_validator| match storage_validator { + Validator::AstarDappStaking(astar_validator) => *astar_validator == validator, + _ => false, + }); + ensure!(is_exist, Error::::ValidatorNotFound); + Ok(().into()) + } + + pub fn do_dapp_staking( + delegator: Delegator, + task: DappStaking, + ) -> DispatchResultWithPostInfo { + let delegator_index = DelegatorIndexByStakingProtocolAndDelegator::::get( + ASTAR_DAPP_STAKING, + delegator.clone(), + ) + .ok_or(Error::::DelegatorIndexNotFound)?; + ensure!( + DelegatorByStakingProtocolAndDelegatorIndex::::contains_key( + ASTAR_DAPP_STAKING, + delegator_index + ), + Error::::DelegatorNotFound + ); + let (call, pending_status) = match task.clone() { + DappStaking::Lock(amount) => ( + AstarCall::::DappStaking(DappStaking::::Lock(amount)).encode(), + Some(PendingStatus::AstarDappStaking(AstarDappStakingPendingStatus::Lock( + delegator.clone(), + amount, + ))), + ), + DappStaking::Unlock(amount) => ( + AstarCall::::DappStaking(DappStaking::::Unlock(amount)).encode(), + Some(PendingStatus::AstarDappStaking(AstarDappStakingPendingStatus::UnLock( + delegator.clone(), + amount, + ))), + ), + DappStaking::ClaimUnlocked => ( + AstarCall::::DappStaking(DappStaking::::ClaimUnlocked).encode(), + Some(PendingStatus::AstarDappStaking( + AstarDappStakingPendingStatus::ClaimUnlocked(delegator.clone()), + )), + ), + DappStaking::Stake(validator, amount) => { + Self::ensure_validator_exist(delegator.clone(), validator.clone())?; + ( + AstarCall::::DappStaking(DappStaking::::Stake( + validator.clone(), + amount, + )) + .encode(), + None, + ) + }, + DappStaking::Unstake(validator, amount) => { + Self::ensure_validator_exist(delegator.clone(), validator.clone())?; + ( + AstarCall::::DappStaking(DappStaking::::Unstake( + validator.clone(), + amount, + )) + .encode(), + None, + ) + }, + DappStaking::ClaimStakerRewards => ( + AstarCall::::DappStaking(DappStaking::::ClaimStakerRewards) + .encode(), + None, + ), + DappStaking::ClaimBonusReward(validator) => { + Self::ensure_validator_exist(delegator.clone(), validator.clone())?; + ( + AstarCall::::DappStaking(DappStaking::::ClaimBonusReward( + validator, + )) + .encode(), + None, + ) + }, + DappStaking::RelockUnlocking => ( + AstarCall::::DappStaking(DappStaking::::RelockUnlocking).encode(), + None, + ), + }; + let (query_id, xcm_message) = + Self::get_query_id_and_xcm_message(call, delegator_index, &pending_status)?; + if let Some(query_id) = query_id { + let pending_status = pending_status.clone().ok_or(Error::::MissingXcmFee)?; + PendingStatusByQueryId::::insert(query_id, pending_status.clone()); + } + Self::send_xcm_message(ASTAR_DAPP_STAKING, xcm_message)?; + Self::deposit_event(Event::::SendXcmTask { + query_id, + delegator, + task: XcmTask::AstarDappStaking(task), + pending_status, + dest_location: ASTAR_DAPP_STAKING.info().remote_dest_location, + }); + Ok(().into()) + } + + pub fn get_query_id_and_xcm_message( + call: Vec, + delegator_index: DelegatorIndex, + pending_status: &Option>, + ) -> Result<(Option, Xcm), Error> { + let call = + Self::wrap_utility_as_derivative_call_data(&ASTAR_DAPP_STAKING, delegator_index, call); + let mut query_id = None; + let xcm_message; + if pending_status.is_some() { + let notify_call = + ::RuntimeCall::from(Call::::notify_astar_dapp_staking { + query_id: 0, + response: Default::default(), + }); + xcm_message = Self::wrap_xcm_message_with_notify( + &ASTAR_DAPP_STAKING, + call, + notify_call, + &mut query_id, + )?; + } else { + xcm_message = Self::wrap_xcm_message(&ASTAR_DAPP_STAKING, call)?; + }; + Ok((query_id, xcm_message)) + } + + pub fn do_notify_astar_dapp_staking( + responder: Location, + pending_status: PendingStatus, + ) -> Result<(), Error> { + let delegator = match pending_status.clone() { + PendingStatus::AstarDappStaking(AstarDappStakingPendingStatus::Lock(delegator, _)) => + delegator, + PendingStatus::AstarDappStaking(AstarDappStakingPendingStatus::UnLock( + delegator, + _, + )) => delegator, + PendingStatus::AstarDappStaking(AstarDappStakingPendingStatus::ClaimUnlocked( + delegator, + )) => delegator, + }; + LedgerByStakingProtocolAndDelegator::::mutate( + ASTAR_DAPP_STAKING, + delegator, + |ledger| -> Result<(), Error> { + if let Some(Ledger::AstarDappStaking(mut pending_ledger)) = ledger.clone() { + match pending_status.clone() { + PendingStatus::AstarDappStaking(AstarDappStakingPendingStatus::Lock( + _, + amount, + )) => { + pending_ledger.add_lock_amount(amount); + }, + PendingStatus::AstarDappStaking(AstarDappStakingPendingStatus::UnLock( + _, + amount, + )) => { + pending_ledger.subtract_lock_amount(amount); + let currency_id = ASTAR_DAPP_STAKING.info().currency_id; + let current_time_unit = + T::VtokenMinting::get_ongoing_time_unit(currency_id) + .ok_or(Error::::TimeUnitNotExist)?; + let configuration = + ConfigurationByStakingProtocol::::get(ASTAR_DAPP_STAKING) + .ok_or(Error::::ConfigurationNotFound)?; + let unlock_time = current_time_unit + .add(configuration.unlock_period) + .ok_or(Error::::TimeUnitNotExist)?; + pending_ledger + .unlocking + .try_push(AstarUnlockingRecord { amount, unlock_time }) + .map_err(|_| Error::::UnlockRecordOverflow)?; + }, + PendingStatus::AstarDappStaking( + AstarDappStakingPendingStatus::ClaimUnlocked(_), + ) => { + let currency_id = ASTAR_DAPP_STAKING.info().currency_id; + let current_time_unit = + T::VtokenMinting::get_ongoing_time_unit(currency_id) + .ok_or(Error::::TimeUnitNotExist)?; + pending_ledger.unlocking.retain(|record| { + current_time_unit.cmp(&record.unlock_time) != Ordering::Greater + }); + }, + }; + *ledger = Some(Ledger::AstarDappStaking(pending_ledger)); + }; + Ok(()) + }, + )?; + Self::deposit_event(Event::::NotifyResponseReceived { responder, pending_status }); + Ok(()) + } +} diff --git a/pallets/slp-v2/src/astar_dapp_staking/mod.rs b/pallets/slp-v2/src/astar_dapp_staking/mod.rs new file mode 100644 index 000000000..b00ddda58 --- /dev/null +++ b/pallets/slp-v2/src/astar_dapp_staking/mod.rs @@ -0,0 +1,20 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +pub mod impls; +pub mod types; diff --git a/pallets/slp-v2/src/astar_dapp_staking/types.rs b/pallets/slp-v2/src/astar_dapp_staking/types.rs new file mode 100644 index 000000000..17c5e2a05 --- /dev/null +++ b/pallets/slp-v2/src/astar_dapp_staking/types.rs @@ -0,0 +1,101 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate::{common::types::Delegator, Config}; +use bifrost_primitives::{Balance, TimeUnit}; +use frame_support::{ + pallet_prelude::{Decode, Encode, MaxEncodedLen, TypeInfo}, + BoundedVec, +}; +use sp_core::{ConstU32, H160}; +use sp_runtime::Saturating; + +/// Multi-VM pointer to smart contract instance. +#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug, Copy, MaxEncodedLen, TypeInfo)] +pub enum AstarValidator { + /// EVM smart contract instance. + Evm(H160), + /// Wasm smart contract instance. + Wasm(AccountId), +} + +/// Dapp staking extrinsic call. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub enum DappStaking { + #[codec(index = 7)] + Lock(#[codec(compact)] Balance), + #[codec(index = 8)] + Unlock(#[codec(compact)] Balance), + #[codec(index = 9)] + ClaimUnlocked, + #[codec(index = 10)] + RelockUnlocking, + #[codec(index = 11)] + Stake(AstarValidator, #[codec(compact)] Balance), + #[codec(index = 12)] + Unstake(AstarValidator, #[codec(compact)] Balance), + #[codec(index = 13)] + ClaimStakerRewards, + #[codec(index = 14)] + ClaimBonusReward(AstarValidator), +} + +/// Astar extrinsic call. +#[derive(Encode, Decode, Debug, Clone)] +pub enum AstarCall { + #[codec(index = 34)] + DappStaking(DappStaking), +} + +/// Astar unlocking record. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub struct AstarUnlockingRecord { + pub amount: Balance, + pub unlock_time: TimeUnit, +} + +/// Astar dapp staking ledger. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Debug, Default, PartialEq, Eq, TypeInfo)] +pub struct AstarDappStakingLedger { + /// How much active locked amount an account has. This can be used for staking. + #[codec(compact)] + pub locked: Balance, + /// Vector of all the unlocking chunks. This is also considered _locked_ but cannot be used for + /// staking. + pub unlocking: BoundedVec>, +} + +impl AstarDappStakingLedger { + /// Adds the specified amount to the total locked amount. + pub fn add_lock_amount(&mut self, amount: Balance) { + self.locked.saturating_accrue(amount); + } + + /// Subtracts the specified amount of the total locked amount. + pub fn subtract_lock_amount(&mut self, amount: Balance) { + self.locked.saturating_reduce(amount); + } +} + +/// PendingStatus in slp protocol. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub enum AstarDappStakingPendingStatus { + Lock(Delegator, Balance), + UnLock(Delegator, Balance), + ClaimUnlocked(Delegator), +} diff --git a/pallets/slp-v2/src/benchmarking.rs b/pallets/slp-v2/src/benchmarking.rs new file mode 100644 index 000000000..ec977e7be --- /dev/null +++ b/pallets/slp-v2/src/benchmarking.rs @@ -0,0 +1,234 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +#![cfg(feature = "runtime-benchmarks")] +use super::*; +use crate::{ + astar_dapp_staking::types::{ + AstarDappStakingLedger, AstarDappStakingPendingStatus, AstarValidator, DappStaking, + }, + common::types::{Ledger, PendingStatus, StakingProtocol, Validator, XcmFee}, + Pallet as SlpV2, +}; +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; +use sp_core::{crypto::Ss58Codec, H160}; +use sp_runtime::{AccountId32 as AccountId, Permill}; +use xcm::v4::MaybeErrorCode; + +pub const STAKING_PROTOCOL: StakingProtocol = StakingProtocol::AstarDappStaking; + +fn do_set_protocol_configuration() +where + ::AccountId: From, +{ + assert_ok!(SlpV2::::set_protocol_configuration( + RawOrigin::Root.into(), + STAKING_PROTOCOL, + ProtocolConfiguration { + xcm_task_fee: XcmFee { weight: Weight::zero(), fee: 100 }, + protocol_fee_rate: Permill::from_perthousand(100), + unlock_period: TimeUnit::Era(9), + operator: AccountId::from([0u8; 32]).into(), + max_update_token_exchange_rate: Permill::from_perthousand(1), + update_time_unit_interval: 100u32, + update_exchange_rate_interval: 100u32, + } + )); +} + +#[benchmarks(where ::AccountId: From)] +mod benchmarks { + use super::*; + + #[benchmark] + fn add_delegator() { + #[extrinsic_call] + _(RawOrigin::Root, STAKING_PROTOCOL, None); + } + + #[benchmark] + fn remove_delegator() -> Result<(), BenchmarkError> { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o") + .unwrap() + .into(), + ); + assert_ok!(SlpV2::::add_delegator(RawOrigin::Root.into(), STAKING_PROTOCOL, None)); + #[extrinsic_call] + _(RawOrigin::Root, STAKING_PROTOCOL, delegator); + + Ok(()) + } + + #[benchmark] + fn add_validator() -> Result<(), BenchmarkError> { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o") + .unwrap() + .into(), + ); + let validator = Validator::AstarDappStaking(AstarValidator::Evm(H160::zero())); + #[extrinsic_call] + _(RawOrigin::Root, STAKING_PROTOCOL, delegator, validator); + Ok(()) + } + + #[benchmark] + fn remove_validator() -> Result<(), BenchmarkError> { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o") + .unwrap() + .into(), + ); + let validator = Validator::AstarDappStaking(AstarValidator::Evm(H160::zero())); + assert_ok!(SlpV2::::add_validator( + RawOrigin::Root.into(), + STAKING_PROTOCOL, + delegator.clone(), + validator.clone() + )); + #[extrinsic_call] + _(RawOrigin::Root, STAKING_PROTOCOL, delegator, validator); + Ok(()) + } + + #[benchmark] + fn set_protocol_configuration() -> Result<(), BenchmarkError> { + #[extrinsic_call] + _( + RawOrigin::Root, + STAKING_PROTOCOL, + ProtocolConfiguration { + xcm_task_fee: XcmFee { weight: Weight::zero(), fee: 100 }, + protocol_fee_rate: Permill::from_perthousand(100), + unlock_period: TimeUnit::Era(9), + operator: AccountId::from([0u8; 32]).into(), + max_update_token_exchange_rate: Permill::from_perthousand(1), + update_time_unit_interval: 100u32, + update_exchange_rate_interval: 100u32, + }, + ); + Ok(()) + } + + #[benchmark] + fn set_ledger() -> Result<(), BenchmarkError> { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o") + .unwrap() + .into(), + ); + let ledger = Ledger::AstarDappStaking(AstarDappStakingLedger { + locked: 100, + unlocking: Default::default(), + }); + assert_ok!(SlpV2::::add_delegator(RawOrigin::Root.into(), STAKING_PROTOCOL, None)); + + #[extrinsic_call] + _(RawOrigin::Root, STAKING_PROTOCOL, delegator, ledger); + Ok(()) + } + + #[benchmark] + fn transfer_to() -> Result<(), BenchmarkError> { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o") + .unwrap() + .into(), + ); + assert_ok!(SlpV2::::add_delegator(RawOrigin::Root.into(), STAKING_PROTOCOL, None)); + #[extrinsic_call] + _(RawOrigin::Root, STAKING_PROTOCOL, delegator); + Ok(()) + } + + #[benchmark] + fn transfer_back() -> Result<(), BenchmarkError> { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o") + .unwrap() + .into(), + ); + assert_ok!(SlpV2::::add_delegator(RawOrigin::Root.into(), STAKING_PROTOCOL, None)); + do_set_protocol_configuration::(); + #[extrinsic_call] + _(RawOrigin::Root, STAKING_PROTOCOL, delegator, 1000); + Ok(()) + } + + #[benchmark] + fn update_ongoing_time_unit() -> Result<(), BenchmarkError> { + #[extrinsic_call] + _(RawOrigin::Root, STAKING_PROTOCOL, Some(TimeUnit::Era(1))); + Ok(()) + } + + #[benchmark] + fn update_token_exchange_rate() -> Result<(), BenchmarkError> { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o") + .unwrap() + .into(), + ); + assert_ok!(SlpV2::::add_delegator(RawOrigin::Root.into(), STAKING_PROTOCOL, None)); + #[extrinsic_call] + _(RawOrigin::Root, STAKING_PROTOCOL, delegator, 1000); + Ok(()) + } + + #[benchmark] + fn astar_dapp_staking() -> Result<(), BenchmarkError> { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o") + .unwrap() + .into(), + ); + assert_ok!(SlpV2::::add_delegator(RawOrigin::Root.into(), STAKING_PROTOCOL, None)); + do_set_protocol_configuration::(); + let task = DappStaking::Lock(100); + #[extrinsic_call] + _(RawOrigin::Root, delegator, task); + Ok(()) + } + + #[benchmark] + fn notify_astar_dapp_staking() -> Result<(), BenchmarkError> { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o") + .unwrap() + .into(), + ); + assert_ok!(SlpV2::::add_delegator(RawOrigin::Root.into(), STAKING_PROTOCOL, None)); + do_set_protocol_configuration::(); + + PendingStatusByQueryId::::insert( + 0, + PendingStatus::AstarDappStaking(AstarDappStakingPendingStatus::Lock( + delegator.clone(), + 100, + )), + ); + #[extrinsic_call] + _(RawOrigin::Root, 0, xcm::v4::Response::DispatchResult(MaybeErrorCode::Success)); + Ok(()) + } + + impl_benchmark_test_suite!(SlpV2, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/slp-v2/src/common/impls.rs b/pallets/slp-v2/src/common/impls.rs new file mode 100644 index 000000000..98778661e --- /dev/null +++ b/pallets/slp-v2/src/common/impls.rs @@ -0,0 +1,354 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate::{ + common::types::{ + Delegator, DelegatorIndex, StakingProtocol, AS_DERIVATIVE_CALL_INDEX, + LIMITED_RESERVE_TRANSFER_ASSETS_CALL_INDEX, + }, + Config, ConfigurationByStakingProtocol, DelegatorByStakingProtocolAndDelegatorIndex, + DelegatorIndexByStakingProtocolAndDelegator, Error, Event, LedgerByStakingProtocolAndDelegator, + NextDelegatorIndexByStakingProtocol, Pallet, +}; +use bifrost_primitives::{Balance, CurrencyId, VtokenMintingOperator}; +use frame_support::{ + dispatch::{DispatchResultWithPostInfo, GetDispatchInfo, RawOrigin}, + ensure, + traits::{EnsureOrigin, Get}, +}; +use frame_system::pallet_prelude::OriginFor; +use orml_traits::{MultiCurrency, XcmTransfer}; +use parity_scale_codec::{Decode, Encode}; +use sp_core::blake2_256; +use sp_runtime::{ + helpers_128bit::multiply_by_rational_with_rounding, traits::TrailingZeroInput, DispatchError, + Rounding, Saturating, +}; +use sp_std::{vec, vec::Vec}; +use xcm::{ + latest::{OriginKind, QueryId, QueryResponseInfo, WeightLimit, WildAsset}, + prelude::{AccountId32, Fungible, Here, ReportTransactStatus}, + v4::{opaque::Xcm, Asset, AssetFilter, AssetId, Assets, Location, SendXcm}, + DoubleEncoded, VersionedAssets, VersionedLocation, +}; + +impl Pallet { + pub fn do_add_delegator( + staking_protocol: StakingProtocol, + delegator: Option>, + ) -> DispatchResultWithPostInfo { + let mut delegator_index = 0; + NextDelegatorIndexByStakingProtocol::::mutate( + staking_protocol, + |index| -> DispatchResultWithPostInfo { + delegator_index = *index; + *index = index.checked_add(1).ok_or(Error::::DelegatorIndexOverflow)?; + let delegator = + delegator.unwrap_or(staking_protocol.get_delegator::(delegator_index)?); + ensure!( + !DelegatorByStakingProtocolAndDelegatorIndex::::contains_key( + staking_protocol, + delegator_index + ), + Error::::DelegatorAlreadyExists + ); + ensure!( + !DelegatorIndexByStakingProtocolAndDelegator::::contains_key( + staking_protocol, + delegator.clone() + ), + Error::::DelegatorIndexAlreadyExists + ); + DelegatorByStakingProtocolAndDelegatorIndex::::insert( + staking_protocol, + delegator_index, + delegator.clone(), + ); + DelegatorIndexByStakingProtocolAndDelegator::::insert( + staking_protocol, + delegator.clone(), + delegator_index, + ); + LedgerByStakingProtocolAndDelegator::::insert( + staking_protocol, + delegator.clone(), + staking_protocol.get_default_ledger(), + ); + Self::deposit_event(Event::AddDelegator { + staking_protocol, + delegator_index, + delegator, + }); + Ok(().into()) + }, + ) + } + + pub fn do_remove_delegator( + staking_protocol: StakingProtocol, + delegator: Delegator, + ) -> DispatchResultWithPostInfo { + let delegator_index = DelegatorIndexByStakingProtocolAndDelegator::::take( + staking_protocol, + delegator.clone(), + ) + .ok_or(Error::::DelegatorIndexNotFound)?; + DelegatorByStakingProtocolAndDelegatorIndex::::take(staking_protocol, delegator_index) + .ok_or(Error::::DelegatorNotFound)?; + LedgerByStakingProtocolAndDelegator::::take(staking_protocol, delegator.clone()) + .ok_or(Error::::LedgerNotFound)?; + Self::deposit_event(Event::RemoveDelegator { + staking_protocol, + delegator_index, + delegator, + }); + Ok(().into()) + } + + pub fn do_transfer_to( + staking_protocol: StakingProtocol, + delegator: Delegator, + ) -> DispatchResultWithPostInfo { + let currency_id = staking_protocol.info().currency_id; + let dest_beneficiary_location = staking_protocol + .get_dest_beneficiary_location::(delegator) + .ok_or(Error::::UnsupportedStakingProtocol)?; + let (entrance_account, _) = T::VtokenMinting::get_entrance_and_exit_accounts(); + let entrance_account_fee_balance = + T::MultiCurrency::free_balance(currency_id, &entrance_account); + T::XcmTransfer::transfer( + entrance_account, + currency_id, + entrance_account_fee_balance, + dest_beneficiary_location, + WeightLimit::Unlimited, + ) + .map_err(|_| Error::::DerivativeAccountIdFailed)?; + Ok(().into()) + } + + pub fn do_transfer_back( + staking_protocol: StakingProtocol, + delegator: Delegator, + amount: Balance, + ) -> DispatchResultWithPostInfo { + let delegator_index = DelegatorIndexByStakingProtocolAndDelegator::::get( + staking_protocol, + delegator.clone(), + ) + .ok_or(Error::::DelegatorIndexNotFound)?; + ensure!( + DelegatorByStakingProtocolAndDelegatorIndex::::contains_key( + staking_protocol, + delegator_index + ), + Error::::DelegatorNotFound + ); + + let transfer_back_call_data = + Self::wrap_polkadot_xcm_limited_reserve_transfer_assets_call_data( + &staking_protocol, + amount, + )?; + let utility_as_derivative_call_data = Self::wrap_utility_as_derivative_call_data( + &staking_protocol, + delegator_index, + transfer_back_call_data, + ); + let xcm_message = + Self::wrap_xcm_message(&staking_protocol, utility_as_derivative_call_data)?; + Self::send_xcm_message(staking_protocol, xcm_message)?; + Ok(().into()) + } + + /// Implemented by Utility pallet to get derived account id + pub fn derivative_account_id( + account_id: T::AccountId, + delegator_index: DelegatorIndex, + ) -> Result> { + let entropy = (b"modlpy/utilisuba", account_id, delegator_index).using_encoded(blake2_256); + let account_id = Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) + .map_err(|_| Error::::DerivativeAccountIdFailed)?; + Ok(account_id) + } + + /// Wrapping any runtime call with as_derivative. + pub fn wrap_utility_as_derivative_call_data( + staking_protocol: &StakingProtocol, + delegator_index: DelegatorIndex, + call: Vec, + ) -> Vec { + let utility_pallet_index = staking_protocol.info().utility_pallet_index; + let mut call_data = utility_pallet_index.encode(); + call_data.extend(AS_DERIVATIVE_CALL_INDEX.encode()); + // derivative index + call_data.extend(delegator_index.encode()); + // runtime call + call_data.extend(call); + call_data + } + + /// Wrapping limited_reserve_transfer_assets + pub fn wrap_polkadot_xcm_limited_reserve_transfer_assets_call_data( + staking_protocol: &StakingProtocol, + amount: Balance, + ) -> Result, Error> { + let polkadot_xcm_pallet_index = staking_protocol.info().polkadot_xcm_pallet_index; + let bifrost_dest_location = staking_protocol.info().bifrost_dest_location; + let (entrance_account, _) = T::VtokenMinting::get_entrance_and_exit_accounts(); + let account_id = entrance_account + .encode() + .try_into() + .map_err(|_| Error::::DerivativeAccountIdFailed)?; + let beneficiary = Location::new(0, AccountId32 { network: None, id: account_id }); + let fee_asset_item = 0u32; + let weight_limit = WeightLimit::Unlimited; + + let mut calldata = polkadot_xcm_pallet_index.encode(); + calldata.extend(LIMITED_RESERVE_TRANSFER_ASSETS_CALL_INDEX.encode()); + // bifrost_dest_location + calldata.extend(VersionedLocation::V4(bifrost_dest_location).encode()); + // beneficiary + calldata.extend(VersionedLocation::V4(beneficiary).encode()); + // native asset + amount + calldata.extend( + VersionedAssets::V4(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(amount), + }])) + .encode(), + ); + // fee_asset_item + calldata.extend(fee_asset_item.encode()); + // weight_limit + calldata.extend(weight_limit.encode()); + Ok(calldata) + } + + /// Wrapping xcm message + /// withdraw_asset + buy_execution + transact + refund_surplus + deposit_asset + pub fn wrap_xcm_message( + staking_protocol: &StakingProtocol, + call: Vec, + ) -> Result> { + let configuration = ConfigurationByStakingProtocol::::get(staking_protocol) + .ok_or(Error::::ConfigurationNotFound)?; + let fee_location = staking_protocol.info().remote_fee_location; + let refund_beneficiary = staking_protocol.info().remote_refund_beneficiary; + let asset = + Asset { id: AssetId(fee_location), fun: Fungible(configuration.xcm_task_fee.fee) }; + let assets: Assets = Assets::from(asset.clone()); + let require_weight_at_most = configuration.xcm_task_fee.weight; + let call: DoubleEncoded<()> = call.into(); + let asset_filter: AssetFilter = AssetFilter::Wild(WildAsset::All); + Ok(Xcm::builder() + .withdraw_asset(assets) + .buy_execution(asset, WeightLimit::Unlimited) + .transact(OriginKind::SovereignAccount, require_weight_at_most, call) + .refund_surplus() + .deposit_asset(asset_filter, refund_beneficiary) + .build()) + } + + /// Wrapping xcm messages with notify + /// withdraw_asset + buy_execution + transact + report_transact_status + refund_surplus + + /// deposit_asset + pub fn wrap_xcm_message_with_notify( + staking_protocol: &StakingProtocol, + call: Vec, + notify_call: ::RuntimeCall, + mut_query_id: &mut Option, + ) -> Result> { + let notify_call_weight = notify_call.get_dispatch_info().weight; + let now = frame_system::Pallet::::block_number(); + let timeout = now.saturating_add(T::QueryTimeout::get()); + let responder = staking_protocol.info().remote_dest_location; + let query_id = + pallet_xcm::Pallet::::new_notify_query(responder, notify_call, timeout, Here); + *mut_query_id = Some(query_id); + let destination = staking_protocol.info().bifrost_dest_location; + let report_transact_status = ReportTransactStatus(QueryResponseInfo { + destination, + query_id, + max_weight: notify_call_weight, + }); + let mut xcm_message = Self::wrap_xcm_message(&staking_protocol, call)?; + xcm_message.0.insert(3, report_transact_status); + Ok(xcm_message) + } + + pub fn send_xcm_message( + staking_protocol: StakingProtocol, + xcm_message: Xcm, + ) -> Result<(), Error> { + let dest_location = staking_protocol.info().remote_dest_location; + let (ticket, _price) = + T::XcmSender::validate(&mut Some(dest_location), &mut Some(xcm_message)) + .map_err(|_| Error::::ErrorValidating)?; + T::XcmSender::deliver(ticket).map_err(|_| Error::::ErrorDelivering)?; + Ok(()) + } + + pub fn calculate_vtoken_amount_by_token_amount( + vtoken_currency_id: CurrencyId, + currency_id: CurrencyId, + token_amount: Balance, + ) -> Result> { + let vtoken_total_issuance = T::MultiCurrency::total_issuance(vtoken_currency_id); + let token_pool_amount = T::VtokenMinting::get_token_pool(currency_id); + // vtoken_amount / vtoken_total_issuance = token_amount / token_pool_amount + // vtoken_amount = token_amount * vtoken_total_issuance / token_pool_amount + let vtoken_amount = multiply_by_rational_with_rounding( + token_amount, + vtoken_total_issuance, + token_pool_amount, + Rounding::Down, + ) + .ok_or(Error::::CalculateProtocolFeeFailed)?; + Ok(vtoken_amount) + } + + pub fn ensure_governance_or_xcm_response( + origin: OriginFor, + ) -> Result { + let responder = T::ResponseOrigin::ensure_origin(origin.clone()) + .or_else(|_| T::ControlOrigin::ensure_origin(origin).map(|_| Here.into()))?; + Ok(responder) + } + + pub fn ensure_governance_or_operator( + origin: OriginFor, + staking_protocol: StakingProtocol, + ) -> Result<(), Error> { + match origin.clone().into() { + Ok(RawOrigin::Signed(signer)) => { + match ConfigurationByStakingProtocol::::get(staking_protocol) { + Some(c) => { + ensure!(c.operator == signer, Error::::NotAuthorized); + Ok(()) + }, + None => Err(Error::::NotAuthorized), + } + }, + _ => { + T::ControlOrigin::ensure_origin(origin).map_err(|_| Error::::NotAuthorized)?; + Ok(()) + }, + } + } +} diff --git a/pallets/slp-v2/src/common/mod.rs b/pallets/slp-v2/src/common/mod.rs new file mode 100644 index 000000000..b00ddda58 --- /dev/null +++ b/pallets/slp-v2/src/common/mod.rs @@ -0,0 +1,20 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +pub mod impls; +pub mod types; diff --git a/pallets/slp-v2/src/common/types/kusama.rs b/pallets/slp-v2/src/common/types/kusama.rs new file mode 100644 index 000000000..21a23dae7 --- /dev/null +++ b/pallets/slp-v2/src/common/types/kusama.rs @@ -0,0 +1,132 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate::{ + common::types::{Delegator, DelegatorIndex, StakingProtocolInfo}, + Config, Error, +}; +use bifrost_primitives::{Balance, TimeUnit, KSM, MOVR}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use polkadot_parachain_primitives::primitives::Sibling; +use scale_info::TypeInfo; +use sp_core::H160; +use sp_runtime::traits::AccountIdConversion; +use xcm::{ + prelude::{AccountId32, AccountKey20, PalletInstance, Parachain}, + v4::Location, +}; + +/// Supported staking protocols. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub enum StakingProtocol { + /// ParachainStaking on Moonriver. + MoonriverParachainStaking, + /// Staking on Kusama + KusamaStaking, +} + +impl StakingProtocol { + pub(crate) fn info(&self) -> StakingProtocolInfo { + match self { + StakingProtocol::MoonriverParachainStaking => StakingProtocolInfo { + utility_pallet_index: 30, + polkadot_xcm_pallet_index: 103, + currency_id: MOVR, + unlock_period: TimeUnit::Round(28), + remote_fee_location: Location::new(0, [PalletInstance(10)]), + remote_refund_beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: Sibling::from(2001).into_account_truncating(), + }], + ), + remote_dest_location: Location::new(1, [Parachain(2023)]), + bifrost_dest_location: Location::new(1, Parachain(2001)), + }, + StakingProtocol::KusamaStaking => StakingProtocolInfo { + utility_pallet_index: 24, + polkadot_xcm_pallet_index: 99, + currency_id: KSM, + unlock_period: TimeUnit::Era(28), + remote_fee_location: Location::here(), + remote_refund_beneficiary: Location::new(0, [Parachain(2001)]), + remote_dest_location: Location::parent(), + bifrost_dest_location: Location::new(0, Parachain(2001)), + }, + } + } + + pub fn get_dest_beneficiary_location( + &self, + delegator: Delegator, + ) -> Option { + match (self, delegator) { + (StakingProtocol::KusamaStaking, Delegator::Substrate(account_id)) => + account_id.encode().try_into().ok().and_then(|account_id| { + Some(Location::new(1, [AccountId32 { network: None, id: account_id }])) + }), + (StakingProtocol::MoonriverParachainStaking, Delegator::Ethereum(account_id)) => + Some(Location::new( + 1, + [ + Parachain(2023), + AccountKey20 { network: None, key: account_id.to_fixed_bytes() }, + ], + )), + _ => None, + } + } + + pub fn get_delegator( + &self, + _delegator_index: DelegatorIndex, + ) -> Result, Error> { + match &self { + _ => unreachable!(), + } + } + + pub fn get_default_ledger(&self) -> Ledger { + match self { + _ => unreachable!(), + } + } +} + +/// Validator in slp protocol. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub enum Validator { + MoonriverParachainStaking(H160), + KusamaStaking(AccountId), +} + +/// Ledger in slp protocol. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub enum Ledger {} + +#[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub enum XcmTask { + Todo(AccountId), +} + +/// PendingStatus in slp protocol. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub enum PendingStatus { + Todo(AccountId), +} diff --git a/pallets/slp-v2/src/common/types/mod.rs b/pallets/slp-v2/src/common/types/mod.rs new file mode 100644 index 000000000..5ba0f8fdc --- /dev/null +++ b/pallets/slp-v2/src/common/types/mod.rs @@ -0,0 +1,78 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use bifrost_primitives::{Balance, BlockNumber, CurrencyId, TimeUnit}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_core::H160; +use sp_runtime::Permill; +use xcm::v4::{Location, Weight}; + +/// Sovereign addresses generate subaccounts via DelegatorIndex +pub type DelegatorIndex = u16; +/// Pallet index in remote chain. +pub type PalletIndex = u8; +pub const AS_DERIVATIVE_CALL_INDEX: u8 = 1; +pub const LIMITED_RESERVE_TRANSFER_ASSETS_CALL_INDEX: u8 = 8; + +#[derive(Encode, Decode, MaxEncodedLen, Default, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub struct ProtocolConfiguration { + pub xcm_task_fee: XcmFee, + pub protocol_fee_rate: Permill, + pub unlock_period: TimeUnit, + pub operator: AccountId, + pub max_update_token_exchange_rate: Permill, + pub update_time_unit_interval: BlockNumber, + pub update_exchange_rate_interval: BlockNumber, +} + +#[derive(Encode, Decode, MaxEncodedLen, Default, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub struct StakingProtocolInfo { + pub utility_pallet_index: PalletIndex, + pub polkadot_xcm_pallet_index: PalletIndex, + pub currency_id: CurrencyId, + pub unlock_period: TimeUnit, + pub remote_fee_location: Location, + pub remote_refund_beneficiary: Location, + pub remote_dest_location: Location, + pub bifrost_dest_location: Location, +} + +/// Delegator account +#[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub enum Delegator { + /// Substrate account + Substrate(AccountId), + /// Ethereum address. + Ethereum(H160), +} + +#[derive(Encode, Decode, MaxEncodedLen, Default, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub struct XcmFee { + pub weight: Weight, + pub fee: Balance, +} + +#[cfg(feature = "kusama")] +pub mod kusama; +#[cfg(feature = "kusama")] +pub use kusama::*; +#[cfg(feature = "polkadot")] +pub mod polkadot; +#[cfg(feature = "polkadot")] +pub use polkadot::*; diff --git a/pallets/slp-v2/src/common/types/polkadot.rs b/pallets/slp-v2/src/common/types/polkadot.rs new file mode 100644 index 000000000..86811140c --- /dev/null +++ b/pallets/slp-v2/src/common/types/polkadot.rs @@ -0,0 +1,173 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate::{ + astar_dapp_staking::types::{ + AstarDappStakingLedger, AstarDappStakingPendingStatus, AstarValidator, DappStaking, + }, + common::types::{Delegator, DelegatorIndex, StakingProtocolInfo}, + Config, Error, +}; +use bifrost_primitives::{TimeUnit, ASTR, DOT, GLMR}; +use frame_support::traits::Get; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use polkadot_parachain_primitives::primitives::Sibling; +use scale_info::TypeInfo; +use sp_core::H160; +use sp_runtime::traits::AccountIdConversion; +use xcm::{ + prelude::{AccountId32, AccountKey20, PalletInstance, Parachain}, + v4::Location, +}; + +/// Supported staking protocols. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub enum StakingProtocol { + /// DappStaking on Astar. + AstarDappStaking, + /// ParachainStaking on Moonbeam. + MoonbeamParachainStaking, + /// Staking on Polkadot + PolkadotStaking, +} + +impl StakingProtocol { + pub(crate) fn info(&self) -> StakingProtocolInfo { + match self { + StakingProtocol::AstarDappStaking => StakingProtocolInfo { + utility_pallet_index: 11, + polkadot_xcm_pallet_index: 51, + currency_id: ASTR, + unlock_period: TimeUnit::Era(9), + remote_fee_location: Location::here(), + remote_refund_beneficiary: Location::new( + 0, + [AccountId32 { + network: None, + id: Sibling::from(2030).into_account_truncating(), + }], + ), + remote_dest_location: Location::new(1, [Parachain(2006)]), + bifrost_dest_location: Location::new(1, Parachain(2030)), + }, + StakingProtocol::MoonbeamParachainStaking => StakingProtocolInfo { + utility_pallet_index: 30, + polkadot_xcm_pallet_index: 103, + currency_id: GLMR, + unlock_period: TimeUnit::Round(28), + remote_fee_location: Location::new(0, [PalletInstance(10)]), + remote_refund_beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: Sibling::from(2030).into_account_truncating(), + }], + ), + remote_dest_location: Location::new(1, [Parachain(2004)]), + bifrost_dest_location: Location::new(1, Parachain(2030)), + }, + StakingProtocol::PolkadotStaking => StakingProtocolInfo { + utility_pallet_index: 26, + polkadot_xcm_pallet_index: 99, + currency_id: DOT, + unlock_period: TimeUnit::Era(28), + remote_fee_location: Location::here(), + remote_refund_beneficiary: Location::new(0, [Parachain(2030)]), + remote_dest_location: Location::parent(), + bifrost_dest_location: Location::new(0, Parachain(2030)), + }, + } + } + + pub fn get_dest_beneficiary_location( + &self, + delegator: Delegator, + ) -> Option { + match (self, delegator) { + (StakingProtocol::AstarDappStaking, Delegator::Substrate(account_id)) => + account_id.encode().try_into().ok().and_then(|account_id| { + Some(Location::new( + 1, + [Parachain(2006), AccountId32 { network: None, id: account_id }], + )) + }), + (StakingProtocol::PolkadotStaking, Delegator::Substrate(account_id)) => + account_id.encode().try_into().ok().and_then(|account_id| { + Some(Location::new(1, [AccountId32 { network: None, id: account_id }])) + }), + (StakingProtocol::MoonbeamParachainStaking, Delegator::Ethereum(account_id)) => + Some(Location::new( + 1, + [ + Parachain(2004), + AccountKey20 { network: None, key: account_id.to_fixed_bytes() }, + ], + )), + _ => None, + } + } + + pub fn get_delegator( + &self, + delegator_index: DelegatorIndex, + ) -> Result, Error> { + match &self { + StakingProtocol::AstarDappStaking => { + let sub_sibling_account = crate::Pallet::::derivative_account_id( + Sibling::from(T::ParachainId::get()).into_account_truncating(), + delegator_index, + )?; + Ok(Delegator::Substrate(sub_sibling_account)) + }, + _ => Err(Error::::UnsupportedStakingProtocol), + } + } + + pub fn get_default_ledger(&self) -> Ledger { + match self { + StakingProtocol::AstarDappStaking => + Ledger::AstarDappStaking(AstarDappStakingLedger::default()), + _ => unreachable!(), + } + } +} + +/// Validator in slp protocol. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub enum Validator { + AstarDappStaking(AstarValidator), + MoonbeamParachainStaking(H160), + PolkadotStaking(AccountId), +} + +/// Ledger in slp protocol. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub enum Ledger { + AstarDappStaking(AstarDappStakingLedger), +} + +#[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub enum XcmTask { + AstarDappStaking(DappStaking), +} + +/// PendingStatus in slp protocol. +#[derive(Encode, Decode, MaxEncodedLen, Clone, Copy, Debug, PartialEq, Eq, TypeInfo)] +pub enum PendingStatus { + AstarDappStaking(AstarDappStakingPendingStatus), +} diff --git a/pallets/slp-v2/src/lib.rs b/pallets/slp-v2/src/lib.rs new file mode 100644 index 000000000..c1a545461 --- /dev/null +++ b/pallets/slp-v2/src/lib.rs @@ -0,0 +1,629 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "polkadot")] +use astar_dapp_staking::types::DappStaking; +use bifrost_primitives::{ + Balance, BlockNumber, CurrencyId, CurrencyIdConversion, TimeUnit, VtokenMintingOperator, +}; +use common::types::{Delegator, DelegatorIndex, ProtocolConfiguration}; +use frame_support::{ + dispatch::{DispatchResultWithPostInfo, GetDispatchInfo}, + pallet_prelude::*, + PalletId, +}; +use frame_system::pallet_prelude::*; +use orml_traits::{MultiCurrency, XcmTransfer}; +use polkadot_parachain_primitives::primitives::Id as ParaId; +use sp_runtime::traits::AccountIdConversion; +pub use weights::WeightInfo; +use xcm::v4::{Location, SendXcm}; + +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(feature = "polkadot")] +mod astar_dapp_staking; +mod common; +#[cfg(test)] +mod tests; +pub mod weights; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use crate::common::types::{Ledger, PendingStatus, StakingProtocol, Validator, XcmTask}; + use sp_runtime::{traits::BlockNumberProvider, Permill}; + use xcm::latest::{MaybeErrorCode, QueryId, Response}; + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_xcm::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type RuntimeOrigin: IsType<::RuntimeOrigin> + + Into::RuntimeOrigin>>; + type RuntimeCall: IsType<::RuntimeCall> + + From> + + GetDispatchInfo; + type ResponseOrigin: EnsureOrigin< + ::RuntimeOrigin, + Success = Location, + >; + type WeightInfo: weights::WeightInfo; + type MultiCurrency: MultiCurrency< + Self::AccountId, + Balance = Balance, + CurrencyId = CurrencyId, + >; + /// The only origin that can modify pallet params + type ControlOrigin: EnsureOrigin<::RuntimeOrigin>; + /// Xcm sender. + type XcmSender: SendXcm; + /// XTokens transfer interface + type XcmTransfer: XcmTransfer; + /// The interface to call VtokenMinting module functions. + type VtokenMinting: VtokenMintingOperator; + /// The currency id conversion. + type CurrencyIdConversion: CurrencyIdConversion; + /// The current block number provider. + type RelaychainBlockNumberProvider: BlockNumberProvider; + /// The query timeout. + #[pallet::constant] + type QueryTimeout: Get>; + /// Commission master Pallet Id to get the commission master account + #[pallet::constant] + type CommissionPalletId: Get; + /// Bifrost parachain id. + #[pallet::constant] + type ParachainId: Get; + /// Maximum validators + #[pallet::constant] + type MaxValidators: Get; + } + + #[pallet::pallet] + pub struct Pallet(_); + + /// Operator for different staking protocols. + #[pallet::storage] + pub type ConfigurationByStakingProtocol = StorageMap< + _, + Blake2_128Concat, + StakingProtocol, + ProtocolConfiguration, + OptionQuery, + >; + + /// StakingProtocol + DelegatorIndex => Delegator + #[pallet::storage] + pub type DelegatorByStakingProtocolAndDelegatorIndex = StorageDoubleMap< + _, + Blake2_128Concat, + StakingProtocol, + Blake2_128Concat, + DelegatorIndex, + Delegator, + OptionQuery, + >; + + /// StakingProtocol + Delegator => DelegatorIndex + #[pallet::storage] + pub type DelegatorIndexByStakingProtocolAndDelegator = StorageDoubleMap< + _, + Blake2_128Concat, + StakingProtocol, + Blake2_128Concat, + Delegator, + DelegatorIndex, + OptionQuery, + >; + + /// StakingProtocol + DelegatorIndex => Delegator + #[pallet::storage] + pub type LedgerByStakingProtocolAndDelegator = StorageDoubleMap< + _, + Blake2_128Concat, + StakingProtocol, + Blake2_128Concat, + Delegator, + Ledger, + OptionQuery, + >; + + /// Validators for different staking protocols. + #[pallet::storage] + pub type ValidatorsByStakingProtocolAndDelegator = StorageDoubleMap< + _, + Blake2_128Concat, + StakingProtocol, + Blake2_128Concat, + Delegator, + BoundedVec, T::MaxValidators>, + ValueQuery, + >; + + /// Next index of different staking protocols. + #[pallet::storage] + pub type NextDelegatorIndexByStakingProtocol = + StorageMap<_, Blake2_128Concat, StakingProtocol, DelegatorIndex, ValueQuery>; + + /// Pending status for different query id. + #[pallet::storage] + pub type PendingStatusByQueryId = + StorageMap<_, Blake2_128Concat, QueryId, PendingStatus, OptionQuery>; + + /// Last update ongoing time unit block number for different staking protocols. + #[pallet::storage] + pub type LastUpdateOngoingTimeUnitBlockNumber = + StorageMap<_, Blake2_128Concat, StakingProtocol, BlockNumber, ValueQuery>; + + /// Last update token exchange rate block number for different staking protocols. + #[pallet::storage] + pub type LastUpdateTokenExchangeRateBlockNumber = StorageDoubleMap< + _, + Blake2_128Concat, + StakingProtocol, + Blake2_128Concat, + Delegator, + BlockNumber, + ValueQuery, + >; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + AddDelegator { + staking_protocol: StakingProtocol, + delegator_index: DelegatorIndex, + delegator: Delegator, + }, + RemoveDelegator { + staking_protocol: StakingProtocol, + delegator_index: DelegatorIndex, + delegator: Delegator, + }, + AddValidator { + staking_protocol: StakingProtocol, + delegator: Delegator, + validator: Validator, + }, + RemoveValidator { + staking_protocol: StakingProtocol, + delegator: Delegator, + validator: Validator, + }, + SetConfiguration { + staking_protocol: StakingProtocol, + configuration: ProtocolConfiguration, + }, + SetLedger { + staking_protocol: StakingProtocol, + delegator: Delegator, + ledger: Ledger, + }, + SendXcmTask { + query_id: Option, + delegator: Delegator, + task: XcmTask, + pending_status: Option>, + dest_location: Location, + }, + NotifyResponseReceived { + responder: Location, + pending_status: PendingStatus, + }, + TimeUnitUpdated { + staking_protocol: StakingProtocol, + time_unit: TimeUnit, + }, + TokenExchangeRateUpdated { + staking_protocol: StakingProtocol, + delegator: Delegator, + protocol_fee_currency_id: CurrencyId, + protocol_fee: Balance, + amount: Balance, + }, + } + + #[pallet::error] + pub enum Error { + /// Delegator index has exceeded the maximum allowed value of 65535. + DelegatorIndexOverflow, + /// The staking protocol is not supported. + UnsupportedStakingProtocol, + /// The delegator index was not found. + DelegatorIndexNotFound, + /// The Configuration was not found. + ConfigurationNotFound, + /// The delegator was not found. + DelegatorNotFound, + /// The ledger was not found. + LedgerNotFound, + /// The validator was not found. + ValidatorNotFound, + /// The delegator already exists. + DelegatorAlreadyExists, + /// The delegator index already exists. + DelegatorIndexAlreadyExists, + /// The validator already exists. + ValidatorAlreadyExists, + /// The maximum number of validators has been reached. + ValidatorsTooMuch, + /// Failed to derive the derivative account ID. + DerivativeAccountIdFailed, + /// Missing XCM fee value. + MissingXcmFee, + /// Missing pending status. + MissingPendingStatus, + /// Missing query ID. + MissingQueryId, + /// Error during validation. + ErrorValidating, + /// Error during delivery. + ErrorDelivering, + /// The specified time unit does not exist. + TimeUnitNotExist, + /// The specified time unit is too short. + UpdateOngoingTimeUnitIntervalTooShort, + /// The specified token exchange rate is too short. + UpdateTokenExchangeRateIntervalTooShort, + /// The specified token exchange rate amount is too large. + UpdateTokenExchangeRateAmountTooLarge, + /// Invalid parameter. + InvalidParameter, + /// calculate protocol fee failed. + CalculateProtocolFeeFailed, + /// Not authorized. + NotAuthorized, + /// IncreaseTokenPoolError + IncreaseTokenPoolError, + /// UnlockRecordOverflow + UnlockRecordOverflow, + } + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet { + /// Set the XCM fee for a specific XCM task. + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::set_protocol_configuration())] + pub fn set_protocol_configuration( + origin: OriginFor, + staking_protocol: StakingProtocol, + configuration: ProtocolConfiguration, + ) -> DispatchResultWithPostInfo { + T::ControlOrigin::ensure_origin(origin)?; + ConfigurationByStakingProtocol::::mutate( + staking_protocol, + |storage_configuration| -> DispatchResultWithPostInfo { + ensure!( + Some(configuration.clone()).ne(storage_configuration), + Error::::InvalidParameter + ); + *storage_configuration = Some(configuration.clone()); + Self::deposit_event(Event::SetConfiguration { + staking_protocol, + configuration, + }); + Ok(().into()) + }, + ) + } + + /// Add a delegator to the staking protocol. + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::add_delegator())] + pub fn add_delegator( + origin: OriginFor, + staking_protocol: StakingProtocol, + delegator: Option>, + ) -> DispatchResultWithPostInfo { + T::ControlOrigin::ensure_origin(origin)?; + Self::do_add_delegator(staking_protocol, delegator) + } + + /// Remove a delegator from the staking protocol. + #[pallet::call_index(2)] + #[pallet::weight(::WeightInfo::remove_delegator())] + pub fn remove_delegator( + origin: OriginFor, + staking_protocol: StakingProtocol, + delegator: Delegator, + ) -> DispatchResultWithPostInfo { + T::ControlOrigin::ensure_origin(origin)?; + Self::do_remove_delegator(staking_protocol, delegator) + } + + /// Add a validator to the staking protocol. + #[pallet::call_index(3)] + #[pallet::weight(::WeightInfo::add_validator())] + pub fn add_validator( + origin: OriginFor, + staking_protocol: StakingProtocol, + delegator: Delegator, + validator: Validator, + ) -> DispatchResultWithPostInfo { + T::ControlOrigin::ensure_origin(origin)?; + ValidatorsByStakingProtocolAndDelegator::::mutate( + staking_protocol, + delegator.clone(), + |validators| -> DispatchResultWithPostInfo { + ensure!(!validators.contains(&validator), Error::::ValidatorAlreadyExists); + validators + .try_push(validator.clone()) + .map_err(|_| Error::::ValidatorsTooMuch)?; + Self::deposit_event(Event::::AddValidator { + staking_protocol, + delegator, + validator, + }); + Ok(().into()) + }, + ) + } + + /// Remove a validator from the staking protocol. + #[pallet::call_index(4)] + #[pallet::weight(::WeightInfo::remove_validator())] + pub fn remove_validator( + origin: OriginFor, + staking_protocol: StakingProtocol, + delegator: Delegator, + validator: Validator, + ) -> DispatchResultWithPostInfo { + T::ControlOrigin::ensure_origin(origin)?; + ValidatorsByStakingProtocolAndDelegator::::mutate( + staking_protocol, + delegator.clone(), + |validators| -> DispatchResultWithPostInfo { + ensure!(validators.contains(&validator), Error::::ValidatorNotFound); + validators.retain(|v| *v != validator); + Self::deposit_event(Event::::RemoveValidator { + staking_protocol, + delegator, + validator, + }); + Ok(().into()) + }, + ) + } + + /// Set the update token exchange rate limit for a specific staking protocol. + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::set_ledger())] + pub fn set_ledger( + origin: OriginFor, + staking_protocol: StakingProtocol, + delegator: Delegator, + ledger: Ledger, + ) -> DispatchResultWithPostInfo { + T::ControlOrigin::ensure_origin(origin)?; + let delegator_index = DelegatorIndexByStakingProtocolAndDelegator::::get( + staking_protocol, + delegator.clone(), + ) + .ok_or(Error::::DelegatorIndexNotFound)?; + ensure!( + DelegatorByStakingProtocolAndDelegatorIndex::::contains_key( + staking_protocol, + delegator_index + ), + Error::::DelegatorNotFound + ); + LedgerByStakingProtocolAndDelegator::::mutate( + staking_protocol, + delegator.clone(), + |storage_ledger| -> DispatchResultWithPostInfo { + ensure!(Some(ledger.clone()).ne(storage_ledger), Error::::InvalidParameter); + *storage_ledger = Some(ledger.clone()); + Self::deposit_event(Event::SetLedger { staking_protocol, delegator, ledger }); + Ok(().into()) + }, + ) + } + + /// Transfer the staking token to remote chain. + #[pallet::call_index(6)] + #[pallet::weight(::WeightInfo::transfer_to())] + pub fn transfer_to( + origin: OriginFor, + staking_protocol: StakingProtocol, + delegator: Delegator, + ) -> DispatchResultWithPostInfo { + Self::ensure_governance_or_operator(origin, staking_protocol)?; + Self::do_transfer_to(staking_protocol, delegator) + } + + /// Transfer the staking token back from remote chain. + #[pallet::call_index(7)] + #[pallet::weight(::WeightInfo::transfer_back())] + pub fn transfer_back( + origin: OriginFor, + staking_protocol: StakingProtocol, + delegator: Delegator, + amount: Balance, + ) -> DispatchResultWithPostInfo { + Self::ensure_governance_or_operator(origin, staking_protocol)?; + Self::do_transfer_back(staking_protocol, delegator, amount) + } + + /// Update the ongoing time unit for a specific staking protocol. + #[pallet::call_index(8)] + #[pallet::weight(::WeightInfo::update_ongoing_time_unit())] + pub fn update_ongoing_time_unit( + origin: OriginFor, + staking_protocol: StakingProtocol, + time_uint_option: Option, + ) -> DispatchResultWithPostInfo { + Self::ensure_governance_or_operator(origin, staking_protocol)?; + let current_block_number = T::RelaychainBlockNumberProvider::current_block_number(); + let update_interval = match ConfigurationByStakingProtocol::::get(staking_protocol) { + Some(configuration) => configuration.update_time_unit_interval, + None => 0, + }; + let last_update_block_number = + LastUpdateOngoingTimeUnitBlockNumber::::get(staking_protocol); + ensure!( + current_block_number >= last_update_block_number + update_interval, + Error::::UpdateOngoingTimeUnitIntervalTooShort + ); + + let currency_id = staking_protocol.info().currency_id; + + let time_unit = match time_uint_option { + Some(time_unit) => time_unit, + None => { + let current_time_unit = T::VtokenMinting::get_ongoing_time_unit(currency_id) + .ok_or(Error::::TimeUnitNotExist)?; + current_time_unit.add_one() + }, + }; + T::VtokenMinting::update_ongoing_time_unit(currency_id, time_unit.clone())?; + LastUpdateOngoingTimeUnitBlockNumber::::insert( + staking_protocol, + current_block_number, + ); + Self::deposit_event(Event::::TimeUnitUpdated { staking_protocol, time_unit }); + Ok(().into()) + } + + /// Update the token exchange rate for a specific staking protocol. + #[pallet::call_index(9)] + #[pallet::weight(::WeightInfo::update_token_exchange_rate())] + pub fn update_token_exchange_rate( + origin: OriginFor, + staking_protocol: StakingProtocol, + delegator: Delegator, + amount: Balance, + ) -> DispatchResultWithPostInfo { + Self::ensure_governance_or_operator(origin, staking_protocol)?; + let currency_id = staking_protocol.info().currency_id; + + // Check the update token exchange rate limit. + let (update_interval, max_update_permill, protocol_fee_rate) = + match ConfigurationByStakingProtocol::::get(staking_protocol) { + Some(configuration) => ( + configuration.update_exchange_rate_interval, + configuration.max_update_token_exchange_rate, + configuration.protocol_fee_rate, + ), + None => (0, Permill::zero(), Permill::zero()), + }; + let current_block_number = T::RelaychainBlockNumberProvider::current_block_number(); + let last_update_block_number = LastUpdateTokenExchangeRateBlockNumber::::get( + staking_protocol, + delegator.clone(), + ); + ensure!( + current_block_number >= last_update_block_number + update_interval, + Error::::UpdateTokenExchangeRateIntervalTooShort + ); + let pool_token_amount = T::VtokenMinting::get_token_pool(currency_id); + let max_amount = max_update_permill.mul_floor(pool_token_amount); + ensure!( + amount <= max_amount || max_amount == 0, + Error::::UpdateTokenExchangeRateAmountTooLarge + ); + + // Charge the protocol fee. + let mut protocol_fee = protocol_fee_rate.mul_floor(amount); + let protocol_fee_currency_id = T::CurrencyIdConversion::convert_to_vtoken(currency_id) + .map_err(|_| Error::::DerivativeAccountIdFailed)?; + if protocol_fee != 0 { + protocol_fee = Self::calculate_vtoken_amount_by_token_amount( + protocol_fee_currency_id, + currency_id, + protocol_fee, + )?; + let protocol_fee_receiver = T::CommissionPalletId::get().into_account_truncating(); + T::MultiCurrency::deposit( + protocol_fee_currency_id, + &protocol_fee_receiver, + protocol_fee, + )?; + } + + // Update the token exchange rate. + T::VtokenMinting::increase_token_pool(currency_id, amount) + .map_err(|_| Error::::IncreaseTokenPoolError)?; + LedgerByStakingProtocolAndDelegator::::mutate( + staking_protocol, + delegator.clone(), + |ledger| match ledger { + #[cfg(feature = "polkadot")] + Some(Ledger::AstarDappStaking(astar_dapp_staking_ledger)) => { + astar_dapp_staking_ledger.add_lock_amount(amount); + Ok(()) + }, + _ => Err(Error::::LedgerNotFound), + }, + )?; + + LastUpdateTokenExchangeRateBlockNumber::::insert( + staking_protocol, + delegator.clone(), + current_block_number, + ); + Self::deposit_event(Event::::TokenExchangeRateUpdated { + staking_protocol, + delegator, + protocol_fee_currency_id, + protocol_fee, + amount, + }); + Ok(().into()) + } + + #[cfg(feature = "polkadot")] + #[pallet::call_index(10)] + #[pallet::weight(::WeightInfo::astar_dapp_staking())] + pub fn astar_dapp_staking( + origin: OriginFor, + delegator: Delegator, + task: DappStaking, + ) -> DispatchResultWithPostInfo { + Self::ensure_governance_or_operator(origin, StakingProtocol::AstarDappStaking)?; + Self::do_dapp_staking(delegator, task) + } + + #[cfg(feature = "polkadot")] + #[pallet::call_index(11)] + #[pallet::weight(::WeightInfo::notify_astar_dapp_staking())] + pub fn notify_astar_dapp_staking( + origin: OriginFor, + query_id: QueryId, + response: Response, + ) -> DispatchResultWithPostInfo { + let responder = Self::ensure_governance_or_xcm_response(origin)?; + let pending_status = PendingStatusByQueryId::::get(query_id) + .ok_or(Error::::MissingPendingStatus)?; + if Response::DispatchResult(MaybeErrorCode::Success) == response { + Self::do_notify_astar_dapp_staking(responder, pending_status)?; + }; + Ok(().into()) + } + } +} diff --git a/pallets/slp-v2/src/mock.rs b/pallets/slp-v2/src/mock.rs new file mode 100644 index 000000000..6e6593591 --- /dev/null +++ b/pallets/slp-v2/src/mock.rs @@ -0,0 +1,323 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate as slp_v2; +use bifrost_asset_registry::AssetIdMaps; +use bifrost_primitives::{ + Amount, Balance, BlockNumber, CurrencyId, DoNothingRouter, MockXcmTransfer, SlpOperator, + SlpxOperator, BNC, DOT, +}; +use frame_support::{ + derive_impl, + pallet_prelude::{ConstU32, Get}, + parameter_types, + traits::{Everything, Nothing}, + PalletId, +}; +use frame_system as system; +use frame_system::EnsureRoot; +use hex_literal::hex; +use pallet_xcm::EnsureResponse; +use polkadot_parachain_primitives::primitives::Id as ParaId; +use sp_core::{crypto::AccountId32, ConstU64}; +use sp_runtime::{ + traits::{BlockNumberProvider, IdentityLookup}, + BuildStorage, +}; +use xcm::{ + prelude::Parachain, + v4::{InteriorLocation, Weight}, +}; +use xcm_builder::{FixedWeightBounds, FrameTransactionalProcessor}; +use xcm_executor::XcmExecutor; + +pub type AccountId = AccountId32; +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + AssetRegistry: bifrost_asset_registry, + Currencies: bifrost_currencies, + VtokenMinting: bifrost_vtoken_minting, + Balances: pallet_balances, + Tokens: orml_tokens, + PolkadotXcm: pallet_xcm, + SlpV2: slp_v2, + } +); + +parameter_types! { + pub const SS58Prefix: u8 = 6; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type Block = Block; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type AccountData = pallet_balances::AccountData; +} + +parameter_types! { + pub const ExistentialDeposit: Balance = 1; +} + +impl pallet_balances::Config for Test { + type AccountStore = frame_system::Pallet; + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; +} + +impl bifrost_asset_registry::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type RegisterOrigin = EnsureRoot; + type WeightInfo = (); +} + +orml_traits::parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance { + 0 + }; +} + +impl orml_tokens::Config for Test { + type Amount = Amount; + type Balance = Balance; + type CurrencyId = CurrencyId; + type DustRemovalWhitelist = Nothing; + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposits = ExistentialDeposits; + type MaxLocks = ConstU32<50>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); + type CurrencyHooks = (); +} + +parameter_types! { + pub const GetNativeCurrencyId: CurrencyId = BNC; +} + +pub type AdaptedBasicCurrency = + bifrost_currencies::BasicCurrencyAdapter; + +impl bifrost_currencies::Config for Test { + type GetNativeCurrencyId = GetNativeCurrencyId; + type MultiCurrency = Tokens; + type NativeCurrency = AdaptedBasicCurrency; + type WeightInfo = (); +} + +parameter_types! { + // One XCM operation is 200_000_000 XcmWeight, cross-chain transfer ~= 2x of transfer = 3_000_000_000 + pub UnitWeightCost: Weight = Weight::from_parts(200_000_000, 0); + pub const MaxInstructions: u32 = 100; + pub UniversalLocation: InteriorLocation = Parachain(2030).into(); + pub CommissionPalletId: PalletId = PalletId(*b"bf/comms"); +} + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type AssetClaims = PolkadotXcm; + type AssetTransactor = (); + type AssetTrap = PolkadotXcm; + type Barrier = (); + type RuntimeCall = RuntimeCall; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type OriginConverter = (); + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type Trader = (); + type Weigher = FixedWeightBounds; + type XcmSender = (); + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = ConstU32<64>; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type AssetLocker = (); + type AssetExchanger = (); + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} + +impl pallet_xcm::Config for Test { + type RuntimeEvent = RuntimeEvent; + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type UniversalLocation = UniversalLocation; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type Weigher = FixedWeightBounds; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Everything; + type XcmRouter = DoNothingRouter; + type XcmTeleportFilter = Nothing; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = ConstU32<2>; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + type AdminOrigin = EnsureRoot; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); +} + +pub struct ParachainId; +impl Get for ParachainId { + fn get() -> ParaId { + 2030.into() + } +} + +// Pallet vtoken-minting configuration +parameter_types! { + pub const MaximumUnlockIdOfUser: u32 = 10; + pub const MaximumUnlockIdOfTimeUnit: u32 = 50; + pub BifrostEntranceAccount: PalletId = PalletId(*b"bf/vtkin"); + pub BifrostExitAccount: PalletId = PalletId(*b"bf/vtout"); + pub BifrostFeeAccount: AccountId = hex!["e4da05f08e89bf6c43260d96f26fffcfc7deae5b465da08669a9d008e64c2c63"].into(); + pub const RelayCurrencyId: CurrencyId = DOT; + pub IncentivePoolAccount: PalletId = PalletId(*b"bf/inpoo"); +} + +pub struct SlpxInterface; +impl SlpxOperator for SlpxInterface { + fn get_moonbeam_transfer_to_fee() -> Balance { + Default::default() + } +} + +pub struct MockSlp; +impl SlpOperator for MockSlp { + fn all_delegation_requests_occupied(_currency_id: CurrencyId) -> bool { + true + } +} + +impl bifrost_vtoken_minting::Config for Test { + type RuntimeEvent = RuntimeEvent; + type MultiCurrency = Currencies; + type ControlOrigin = EnsureRoot; + type MaximumUnlockIdOfUser = MaximumUnlockIdOfUser; + type MaximumUnlockIdOfTimeUnit = MaximumUnlockIdOfTimeUnit; + type EntranceAccount = BifrostEntranceAccount; + type ExitAccount = BifrostExitAccount; + type FeeAccount = BifrostFeeAccount; + type RedeemFeeAccount = BifrostFeeAccount; + type RelayChainToken = RelayCurrencyId; + type CurrencyIdConversion = AssetIdMaps; + type CurrencyIdRegister = AssetIdMaps; + type BifrostSlp = MockSlp; + type BifrostSlpx = SlpxInterface; + type WeightInfo = (); + type OnRedeemSuccess = (); + type XcmTransfer = MockXcmTransfer; + type AstarParachainId = ConstU32<2007>; + type MoonbeamParachainId = ConstU32<2023>; + type HydradxParachainId = ConstU32<2034>; + type MantaParachainId = ConstU32<2104>; + type InterlayParachainId = ConstU32<2032>; + type ChannelCommission = (); + type MaxLockRecords = ConstU32<100>; + type IncentivePoolAccount = IncentivePoolAccount; + type VeMinting = (); + type AssetIdMaps = AssetIdMaps; +} + +parameter_types! { + pub static RelaychainBlockNumber: BlockNumber = 1; + +} + +pub struct RelaychainDataProvider; + +impl RelaychainDataProvider { + pub fn set_block_number(block: BlockNumber) { + RelaychainBlockNumber::set(block); + } +} + +impl BlockNumberProvider for RelaychainDataProvider { + type BlockNumber = BlockNumber; + + fn current_block_number() -> Self::BlockNumber { + RelaychainBlockNumber::get() + } +} + +impl slp_v2::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type XcmSender = DoNothingRouter; + type WeightInfo = (); + type MultiCurrency = Tokens; + type ControlOrigin = EnsureRoot; + type ParachainId = ParachainId; + type ResponseOrigin = EnsureResponse; + type QueryTimeout = ConstU64<100>; + type VtokenMinting = VtokenMinting; + type XcmTransfer = MockXcmTransfer; + type CurrencyIdConversion = AssetIdMaps; + type CommissionPalletId = CommissionPalletId; + type RelaychainBlockNumberProvider = RelaychainDataProvider; + type MaxValidators = ConstU32<256>; +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = system::GenesisConfig::::default().build_storage().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +pub(crate) fn last_event() -> RuntimeEvent { + system::Pallet::::events().pop().expect("Event expected").event +} + +pub(crate) fn expect_event>(e: E) { + assert_eq!(last_event(), e.into()); +} diff --git a/pallets/slp-v2/src/tests.rs b/pallets/slp-v2/src/tests.rs new file mode 100644 index 000000000..49d626fbf --- /dev/null +++ b/pallets/slp-v2/src/tests.rs @@ -0,0 +1,939 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use crate::{ + astar_dapp_staking::types::{ + AstarDappStakingLedger, AstarDappStakingPendingStatus, AstarUnlockingRecord, + AstarValidator, DappStaking, + }, + common::types::{ + Delegator, Ledger, PendingStatus, ProtocolConfiguration, StakingProtocol, Validator, + XcmFee, XcmTask, + }, + mock::*, + DelegatorByStakingProtocolAndDelegatorIndex, DelegatorIndexByStakingProtocolAndDelegator, + Error as SlpV2Error, Event as SlpV2Event, LastUpdateOngoingTimeUnitBlockNumber, + LedgerByStakingProtocolAndDelegator, NextDelegatorIndexByStakingProtocol, + ValidatorsByStakingProtocolAndDelegator, +}; +use bifrost_primitives::{TimeUnit, VtokenMintingOperator, VASTR}; +use cumulus_primitives_core::Weight; +use frame_support::{assert_noop, assert_ok, traits::fungibles::Mutate}; +use orml_traits::MultiCurrency; +use pallet_xcm::Origin as XcmOrigin; +use polkadot_parachain_primitives::primitives::Sibling; +use sp_core::{bytes::to_hex, crypto::Ss58Codec, H160}; +use sp_runtime::{ + helpers_128bit::multiply_by_rational_with_rounding, traits::AccountIdConversion, BoundedVec, + Permill, Rounding, +}; +use xcm::{ + latest::{MaybeErrorCode, Parent, Response}, + prelude::{AccountId32, Parachain}, + v4::Location, +}; + +pub const STAKING_PROTOCOL: StakingProtocol = StakingProtocol::AstarDappStaking; + +pub const CONFIGURATION: ProtocolConfiguration = ProtocolConfiguration { + xcm_task_fee: XcmFee { weight: Weight::zero(), fee: 100 }, + protocol_fee_rate: Permill::from_perthousand(100), + unlock_period: TimeUnit::Era(9), + operator: AccountId::new([0u8; 32]), + max_update_token_exchange_rate: Permill::from_perthousand(1), + update_time_unit_interval: 100u32, + update_exchange_rate_interval: 100u32, +}; + +fn set_protocol_configuration() { + assert_ok!(SlpV2::set_protocol_configuration( + RuntimeOrigin::root(), + STAKING_PROTOCOL, + CONFIGURATION + )); +} + +#[test] +fn derivative_account_id_should_work() { + new_test_ext().execute_with(|| { + let sbling2030: AccountId = Sibling::from(2030).into_account_truncating(); + let sub_0_sbling2030 = SlpV2::derivative_account_id(sbling2030.clone(), 0).unwrap(); + let sub_1_sbling2030 = SlpV2::derivative_account_id(sbling2030.clone(), 1).unwrap(); + let sub_2_sbling2030 = SlpV2::derivative_account_id(sbling2030.clone(), 2).unwrap(); + + assert_eq!( + sub_0_sbling2030, + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap() + ); + assert_eq!( + sub_1_sbling2030, + AccountId::from_ss58check("XF713iFjaLwTxvVQv3YJdKhFY4EYpcVh6GzAWR7Lj5aoNHZ").unwrap() + ); + assert_eq!( + sub_2_sbling2030, + AccountId::from_ss58check("YeKP2BdVpFrXbbqkoVhDFZP9u3nUuop7fpMppQczQXBLhD1").unwrap() + ) + }) +} + +#[test] +fn set_configuration_should_work() { + new_test_ext().execute_with(|| { + set_protocol_configuration(); + expect_event(SlpV2Event::SetConfiguration { + staking_protocol: STAKING_PROTOCOL, + configuration: CONFIGURATION, + }); + }) +} + +#[test] +fn add_delegator_should_work() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let delegator_index = 0; + + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None)); + expect_event(SlpV2Event::AddDelegator { + staking_protocol: STAKING_PROTOCOL, + delegator_index, + delegator: delegator.clone(), + }); + assert_eq!( + DelegatorByStakingProtocolAndDelegatorIndex::::get( + STAKING_PROTOCOL, + delegator_index + ), + Some(delegator.clone()) + ); + assert_eq!( + DelegatorIndexByStakingProtocolAndDelegator::::get( + STAKING_PROTOCOL, + delegator.clone() + ), + Some(delegator_index) + ); + assert_eq!(NextDelegatorIndexByStakingProtocol::::get(STAKING_PROTOCOL), 1); + assert_eq!( + LedgerByStakingProtocolAndDelegator::::get(STAKING_PROTOCOL, delegator), + Some(Ledger::AstarDappStaking(AstarDappStakingLedger { + locked: 0, + unlocking: Default::default() + })) + ); + }); +} + +#[test] +fn repeat_add_delegator_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(SlpV2::add_delegator( + RuntimeOrigin::root(), + StakingProtocol::AstarDappStaking, + None + )); + + let delegator = Delegator::Substrate( + AccountId::from_ss58check("XF713iFjaLwTxvVQv3YJdKhFY4EYpcVh6GzAWR7Lj5aoNHZ").unwrap(), + ); + let delegator_index = 1; + + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None)); + expect_event(SlpV2Event::AddDelegator { + staking_protocol: STAKING_PROTOCOL, + delegator_index, + delegator: delegator.clone(), + }); + assert_eq!( + DelegatorByStakingProtocolAndDelegatorIndex::::get( + STAKING_PROTOCOL, + delegator_index + ), + Some(delegator.clone()) + ); + assert_eq!( + DelegatorIndexByStakingProtocolAndDelegator::::get( + STAKING_PROTOCOL, + delegator.clone() + ), + Some(delegator_index) + ); + assert_eq!(NextDelegatorIndexByStakingProtocol::::get(STAKING_PROTOCOL), 2); + assert_eq!( + LedgerByStakingProtocolAndDelegator::::get(STAKING_PROTOCOL, delegator), + Some(Ledger::AstarDappStaking(AstarDappStakingLedger { + locked: 0, + unlocking: Default::default() + })) + ); + }); +} + +#[test] +fn add_delegator_delegator_index_over_flow() { + new_test_ext().execute_with(|| { + NextDelegatorIndexByStakingProtocol::::insert(STAKING_PROTOCOL, 65535); + assert_noop!( + SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None), + SlpV2Error::::DelegatorIndexOverflow + ); + }); +} + +#[test] +fn add_delegator_delegator_already_exists() { + new_test_ext().execute_with(|| { + let delegator_0 = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + + DelegatorByStakingProtocolAndDelegatorIndex::::insert( + STAKING_PROTOCOL, + 0, + delegator_0, + ); + assert_noop!( + SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None), + SlpV2Error::::DelegatorAlreadyExists + ); + }); +} + +#[test] +fn add_delegator_delegator_index_already_exists() { + new_test_ext().execute_with(|| { + let delegator_0 = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + + DelegatorIndexByStakingProtocolAndDelegator::::insert( + STAKING_PROTOCOL, + delegator_0, + 0, + ); + assert_noop!( + SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None), + SlpV2Error::::DelegatorIndexAlreadyExists + ); + }); +} + +#[test] +fn remove_delegator_should_work() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let delegator_index = 0; + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None)); + assert_ok!(SlpV2::remove_delegator( + RuntimeOrigin::root(), + STAKING_PROTOCOL, + delegator.clone() + )); + expect_event(SlpV2Event::RemoveDelegator { + staking_protocol: STAKING_PROTOCOL, + delegator_index, + delegator: delegator.clone(), + }); + assert_eq!( + DelegatorByStakingProtocolAndDelegatorIndex::::get( + STAKING_PROTOCOL, + delegator_index + ), + None + ); + assert_eq!( + DelegatorIndexByStakingProtocolAndDelegator::::get(STAKING_PROTOCOL, delegator), + None + ); + assert_eq!(NextDelegatorIndexByStakingProtocol::::get(STAKING_PROTOCOL), 1); + }); +} + +#[test] +fn remove_delegator_delegator_index_not_found() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + assert_noop!( + SlpV2::remove_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, delegator.clone()), + SlpV2Error::::DelegatorIndexNotFound + ); + }); +} + +#[test] +fn remove_delegator_delegator_not_found() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + DelegatorIndexByStakingProtocolAndDelegator::::insert( + STAKING_PROTOCOL, + delegator.clone(), + 0, + ); + assert_noop!( + SlpV2::remove_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, delegator.clone()), + SlpV2Error::::DelegatorNotFound + ); + }); +} + +#[test] +fn add_validator_should_work() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let validator = Validator::AstarDappStaking(AstarValidator::Evm(H160::default())); + + assert_ok!(SlpV2::add_validator( + RuntimeOrigin::root(), + STAKING_PROTOCOL, + delegator.clone(), + validator.clone() + )); + expect_event(SlpV2Event::AddValidator { + staking_protocol: STAKING_PROTOCOL, + delegator: delegator.clone(), + validator: validator.clone(), + }); + assert_eq!( + ValidatorsByStakingProtocolAndDelegator::::get(STAKING_PROTOCOL, delegator) + .to_vec(), + vec![validator] + ); + }); +} + +#[test] +fn repeat_add_validator_should_work() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let validator1 = Validator::AstarDappStaking(AstarValidator::Evm(H160::default())); + let validator2 = Validator::AstarDappStaking(AstarValidator::Wasm( + AccountId::from_ss58check("YeKP2BdVpFrXbbqkoVhDFZP9u3nUuop7fpMppQczQXBLhD1").unwrap(), + )); + + assert_ok!(SlpV2::add_validator( + RuntimeOrigin::root(), + STAKING_PROTOCOL, + delegator.clone(), + validator1.clone() + )); + assert_ok!(SlpV2::add_validator( + RuntimeOrigin::root(), + STAKING_PROTOCOL, + delegator.clone(), + validator2.clone() + )); + + expect_event(SlpV2Event::AddValidator { + staking_protocol: STAKING_PROTOCOL, + delegator: delegator.clone(), + validator: validator2.clone(), + }); + assert_eq!( + ValidatorsByStakingProtocolAndDelegator::::get( + STAKING_PROTOCOL, + delegator.clone() + ) + .to_vec(), + vec![validator1, validator2] + ); + }); +} + +#[test] +fn remove_validator_should_work() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let validator = Validator::AstarDappStaking(AstarValidator::Evm(H160::default())); + + assert_ok!(SlpV2::add_validator( + RuntimeOrigin::root(), + STAKING_PROTOCOL, + delegator.clone(), + validator.clone() + )); + assert_ok!(SlpV2::remove_validator( + RuntimeOrigin::root(), + STAKING_PROTOCOL, + delegator.clone(), + validator.clone() + )); + expect_event(SlpV2Event::RemoveValidator { + staking_protocol: STAKING_PROTOCOL, + delegator: delegator.clone(), + validator: validator.clone(), + }); + assert_eq!( + ValidatorsByStakingProtocolAndDelegator::::get( + STAKING_PROTOCOL, + delegator.clone() + ) + .to_vec(), + vec![] + ); + }); +} + +#[test] +fn astar_dapp_staking_lock() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let task = DappStaking::Lock(100); + let pending_status = PendingStatus::AstarDappStaking(AstarDappStakingPendingStatus::Lock( + delegator.clone(), + 100, + )); + let dest_location = STAKING_PROTOCOL.info().remote_dest_location; + + set_protocol_configuration(); + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None)); + + assert_ok!(SlpV2::astar_dapp_staking( + RuntimeOrigin::root(), + delegator.clone(), + task.clone() + )); + expect_event(SlpV2Event::SendXcmTask { + query_id: Some(0), + delegator: delegator.clone(), + task: XcmTask::AstarDappStaking(task), + pending_status: Some(pending_status), + dest_location, + }); + assert_ok!(SlpV2::notify_astar_dapp_staking( + XcmOrigin::Response(Parent.into()).into(), + 0, + Response::DispatchResult(MaybeErrorCode::Success) + )); + + let ledger = + LedgerByStakingProtocolAndDelegator::::get(STAKING_PROTOCOL, delegator).unwrap(); + assert_eq!( + ledger, + Ledger::AstarDappStaking(AstarDappStakingLedger { + locked: 100, + unlocking: Default::default() + }) + ) + }) +} + +#[test] +fn repeat_astar_dapp_staking_lock() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let task1 = DappStaking::Lock(100); + let task2 = DappStaking::Lock(200); + let query_id_0 = 0; + let query_id_1 = 1; + let pending_status = PendingStatus::AstarDappStaking(AstarDappStakingPendingStatus::Lock( + delegator.clone(), + 200, + )); + let dest_location = STAKING_PROTOCOL.info().remote_dest_location; + set_protocol_configuration(); + + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None)); + + assert_ok!(SlpV2::astar_dapp_staking(RuntimeOrigin::root(), delegator.clone(), task1)); + assert_ok!(SlpV2::notify_astar_dapp_staking( + XcmOrigin::Response(Parent.into()).into(), + query_id_0, + Response::DispatchResult(MaybeErrorCode::Success) + )); + + assert_ok!(SlpV2::astar_dapp_staking( + RuntimeOrigin::root(), + delegator.clone(), + task2.clone() + )); + expect_event(SlpV2Event::SendXcmTask { + query_id: Some(query_id_1), + delegator: delegator.clone(), + task: XcmTask::AstarDappStaking(task2), + pending_status: Some(pending_status), + dest_location, + }); + assert_ok!(SlpV2::notify_astar_dapp_staking( + XcmOrigin::Response(Parent.into()).into(), + query_id_1, + Response::DispatchResult(MaybeErrorCode::Success) + )); + + let ledger = + LedgerByStakingProtocolAndDelegator::::get(STAKING_PROTOCOL, delegator).unwrap(); + assert_eq!( + ledger, + Ledger::AstarDappStaking(AstarDappStakingLedger { + locked: 300, + unlocking: Default::default() + }) + ) + }) +} + +#[test] +fn astar_dapp_staking_unlock() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let task = DappStaking::Lock(100); + + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None)); + set_protocol_configuration(); + + assert_ok!(SlpV2::astar_dapp_staking(RuntimeOrigin::root(), delegator.clone(), task)); + assert_ok!(SlpV2::notify_astar_dapp_staking( + XcmOrigin::Response(Parent.into()).into(), + 0, + Response::DispatchResult(MaybeErrorCode::Success) + )); + + let task = DappStaking::Unlock(50); + RelaychainBlockNumber::set(100); + assert_ok!(SlpV2::update_ongoing_time_unit( + RuntimeOrigin::root(), + STAKING_PROTOCOL, + Some(TimeUnit::Era(1)) + )); + assert_ok!(SlpV2::astar_dapp_staking(RuntimeOrigin::root(), delegator.clone(), task)); + assert_ok!(SlpV2::notify_astar_dapp_staking( + XcmOrigin::Response(Parent.into()).into(), + 1, + Response::DispatchResult(MaybeErrorCode::Success) + )); + + let ledger = + LedgerByStakingProtocolAndDelegator::::get(STAKING_PROTOCOL, delegator).unwrap(); + assert_eq!( + ledger, + Ledger::AstarDappStaking(AstarDappStakingLedger { + locked: 50, + unlocking: BoundedVec::try_from(vec![AstarUnlockingRecord { + amount: 50, + unlock_time: TimeUnit::Era(10) + }]) + .unwrap() + }) + ) + }) +} + +#[test] +fn astar_dapp_staking_stake() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let validator = Validator::AstarDappStaking(AstarValidator::Evm(H160::default())); + let task = DappStaking::Stake(AstarValidator::Evm(H160::default()), 100); + let query_id = None; + let pending_status = None; + let dest_location = STAKING_PROTOCOL.info().remote_dest_location; + + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None)); + assert_ok!(SlpV2::add_validator( + RuntimeOrigin::root(), + STAKING_PROTOCOL, + delegator.clone(), + validator.clone() + )); + set_protocol_configuration(); + + assert_ok!(SlpV2::astar_dapp_staking( + RuntimeOrigin::root(), + delegator.clone(), + task.clone() + )); + expect_event(SlpV2Event::SendXcmTask { + query_id, + delegator, + task: XcmTask::AstarDappStaking(task.clone()), + pending_status, + dest_location, + }) + }) +} + +#[test] +fn astar_dapp_staking_unstake() { + new_test_ext().execute_with(|| { + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let validator = Validator::AstarDappStaking(AstarValidator::Evm(H160::default())); + let task = DappStaking::Unstake(AstarValidator::Evm(H160::default()), 100); + let query_id = None; + let pending_status = None; + let dest_location = STAKING_PROTOCOL.info().remote_dest_location; + + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None)); + assert_ok!(SlpV2::add_validator( + RuntimeOrigin::root(), + STAKING_PROTOCOL, + delegator.clone(), + validator.clone() + )); + set_protocol_configuration(); + + assert_ok!(SlpV2::astar_dapp_staking( + RuntimeOrigin::root(), + delegator.clone(), + task.clone() + )); + expect_event(SlpV2Event::SendXcmTask { + query_id, + delegator, + task: XcmTask::AstarDappStaking(task.clone()), + pending_status, + dest_location, + }) + }) +} + +#[test] +fn staking_protocol_get_dest_beneficiary_location() { + new_test_ext().execute_with(|| { + let staking_protocol = StakingProtocol::AstarDappStaking; + let account_id = + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(); + let delegator = Delegator::Substrate(account_id.clone()); + assert_eq!( + staking_protocol.get_dest_beneficiary_location::(delegator), + Some(Location::new( + 1, + [Parachain(2006), AccountId32 { network: None, id: account_id.into() }] + )) + ); + }) +} + +#[test] +fn astar_polkadot_xcm_call() { + new_test_ext().execute_with(|| { + let calldata = SlpV2::wrap_polkadot_xcm_limited_reserve_transfer_assets_call_data( + &StakingProtocol::AstarDappStaking, + 100, + ) + .unwrap(); + + assert_eq!(to_hex(&calldata, false), "0x330804010100b91f04000101006d6f646c62662f76746b696e0000000000000000000000000000000000000000040400000091010000000000"); + + let call_data = SlpV2::wrap_polkadot_xcm_limited_reserve_transfer_assets_call_data( + &StakingProtocol::PolkadotStaking, + 100, + ) + .unwrap(); + assert_eq!(to_hex(&call_data, false), "0x630804000100b91f04000101006d6f646c62662f76746b696e0000000000000000000000000000000000000000040400000091010000000000"); + }) +} + +#[test] +fn set_ledger_should_work() { + new_test_ext().execute_with(|| { + let staking_protocol = StakingProtocol::AstarDappStaking; + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let ledger = Ledger::AstarDappStaking(AstarDappStakingLedger { + locked: 100, + unlocking: Default::default(), + }); + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), staking_protocol, None)); + assert_ok!(SlpV2::set_ledger( + RuntimeOrigin::root(), + staking_protocol, + delegator.clone(), + ledger.clone() + )); + + expect_event(SlpV2Event::SetLedger { + staking_protocol, + delegator: delegator.clone(), + ledger: ledger.clone(), + }); + assert_eq!( + LedgerByStakingProtocolAndDelegator::::get(staking_protocol, delegator.clone()), + Some(ledger) + ); + }) +} + +#[test] +fn set_ledger_error() { + new_test_ext().execute_with(|| { + let staking_protocol = StakingProtocol::AstarDappStaking; + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let ledger = Ledger::AstarDappStaking(AstarDappStakingLedger { + locked: 100, + unlocking: Default::default(), + }); + assert_noop!( + SlpV2::set_ledger( + RuntimeOrigin::root(), + staking_protocol, + delegator.clone(), + ledger.clone() + ), + SlpV2Error::::DelegatorIndexNotFound + ); + + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), staking_protocol, None)); + assert_ok!(SlpV2::set_ledger( + RuntimeOrigin::root(), + staking_protocol, + delegator.clone(), + ledger.clone() + )); + + assert_noop!( + SlpV2::set_ledger( + RuntimeOrigin::root(), + staking_protocol, + delegator.clone(), + ledger.clone() + ), + SlpV2Error::::InvalidParameter + ); + }) +} + +#[test] +fn update_ongoing_time_unit_should_work() { + new_test_ext().execute_with(|| { + let staking_protocol = StakingProtocol::AstarDappStaking; + let currency_id = staking_protocol.info().currency_id; + set_protocol_configuration(); + RelaychainDataProvider::set_block_number(100); + assert_ok!(SlpV2::update_ongoing_time_unit( + RuntimeOrigin::root(), + staking_protocol, + Some(TimeUnit::Era(1)) + )); + expect_event(SlpV2Event::TimeUnitUpdated { staking_protocol, time_unit: TimeUnit::Era(1) }); + assert_eq!(VtokenMinting::get_ongoing_time_unit(currency_id), Some(TimeUnit::Era(1))); + assert_eq!(LastUpdateOngoingTimeUnitBlockNumber::::get(staking_protocol), 100); + + RelaychainDataProvider::set_block_number(200); + + assert_ok!(SlpV2::update_ongoing_time_unit(RuntimeOrigin::root(), staking_protocol, None)); + expect_event(SlpV2Event::TimeUnitUpdated { staking_protocol, time_unit: TimeUnit::Era(2) }); + assert_eq!(VtokenMinting::get_ongoing_time_unit(currency_id), Some(TimeUnit::Era(2))); + assert_eq!(LastUpdateOngoingTimeUnitBlockNumber::::get(staking_protocol), 200); + }); +} + +#[test] +fn update_ongoing_time_unit_update_interval_too_short() { + new_test_ext().execute_with(|| { + let staking_protocol = StakingProtocol::AstarDappStaking; + set_protocol_configuration(); + + // current relaychain block number 1 < update_interval 100 + last update block number 0 => + // Error + assert_noop!( + SlpV2::update_ongoing_time_unit( + RuntimeOrigin::root(), + staking_protocol, + Some(TimeUnit::Era(1)) + ), + SlpV2Error::::UpdateOngoingTimeUnitIntervalTooShort + ); + + RelaychainDataProvider::set_block_number(100); + // current relaychain block number 100 = update_interval 100 + last update block number 0 => + // Ok + assert_noop!( + SlpV2::update_ongoing_time_unit(RuntimeOrigin::root(), staking_protocol, None), + SlpV2Error::::TimeUnitNotExist + ); + + assert_ok!(SlpV2::update_ongoing_time_unit( + RuntimeOrigin::root(), + staking_protocol, + Some(TimeUnit::Era(1)) + )); + + RelaychainDataProvider::set_block_number(199); + // current relaychain block number 199 < update_interval 100 + last update block number 100 + // => Error + assert_noop!( + SlpV2::update_ongoing_time_unit(RuntimeOrigin::root(), staking_protocol, None), + SlpV2Error::::UpdateOngoingTimeUnitIntervalTooShort + ); + RelaychainDataProvider::set_block_number(200); + // current relaychain block number 200 = update_interval 100 + last update block number 100 + // => Ok + assert_ok!(SlpV2::update_ongoing_time_unit(RuntimeOrigin::root(), staking_protocol, None)); + }); +} + +#[test] +fn update_token_exchange_rate_should_work() { + new_test_ext().execute_with(|| { + let staking_protocol = StakingProtocol::AstarDappStaking; + let currency_id = staking_protocol.info().currency_id; + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let amount = 10_059_807_133_828_175_000_000u128; + let token_pool = 24_597_119_664_064_597_684_680_531u128; + let vtoken_total_issuance = 21_728_134_208_272_171_009_169_962u128; + + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None)); + Currencies::set_balance(VASTR, &AccountId::from([0u8; 32]), vtoken_total_issuance); + assert_eq!(Currencies::total_issuance(VASTR), vtoken_total_issuance); + assert_ok!(VtokenMinting::increase_token_pool(currency_id, token_pool)); + + set_protocol_configuration(); + assert_eq!(VtokenMinting::get_token_pool(currency_id), token_pool); + + RelaychainDataProvider::set_block_number(100); + + // Set protocol fee rate is 10% + let protocol_fee_rate = Permill::from_perthousand(100); + assert_ok!(SlpV2::update_token_exchange_rate( + RuntimeOrigin::root(), + staking_protocol, + delegator.clone(), + amount + )); + // The protocol_fee is 888.644046532367789159 VASTR. + let protocol_fee = multiply_by_rational_with_rounding( + protocol_fee_rate * amount, + vtoken_total_issuance, + token_pool, + Rounding::Down, + ) + .unwrap(); + expect_event(SlpV2Event::TokenExchangeRateUpdated { + staking_protocol, + delegator: delegator.clone(), + protocol_fee_currency_id: VASTR, + protocol_fee, + amount, + }); + let vtoken_total_issuance = vtoken_total_issuance + protocol_fee; + let token_pool = token_pool + amount; + assert_eq!(VtokenMinting::get_token_pool(currency_id), token_pool); + assert_eq!(Currencies::total_issuance(VASTR), vtoken_total_issuance); + assert_eq!( + Currencies::free_balance(VASTR, &CommissionPalletId::get().into_account_truncating()), + protocol_fee + ); + + RelaychainDataProvider::set_block_number(200); + // current relaychain block number 300 = update_interval 100 + last update block number 200 + // => Ok + assert_ok!(SlpV2::update_token_exchange_rate( + RuntimeOrigin::root(), + staking_protocol, + delegator.clone(), + amount + )); + + // The protocol_fee is 888.317083868634496826 VASTR. + let protocol_fee_1 = multiply_by_rational_with_rounding( + protocol_fee_rate * amount, + vtoken_total_issuance, + token_pool, + Rounding::Down, + ) + .unwrap(); + expect_event(SlpV2Event::TokenExchangeRateUpdated { + staking_protocol, + delegator: delegator.clone(), + protocol_fee_currency_id: VASTR, + protocol_fee: protocol_fee_1, + amount, + }); + let vtoken_total_issuance = vtoken_total_issuance + protocol_fee_1; + let token_pool = token_pool + amount; + assert_eq!(VtokenMinting::get_token_pool(currency_id), token_pool); + assert_eq!(Currencies::total_issuance(VASTR), vtoken_total_issuance); + assert_eq!( + Currencies::free_balance(VASTR, &CommissionPalletId::get().into_account_truncating()), + protocol_fee + protocol_fee_1 + ); + }) +} + +#[test] +fn update_token_exchange_rate_limt_error() { + new_test_ext().execute_with(|| { + let staking_protocol = StakingProtocol::AstarDappStaking; + let currency_id = staking_protocol.info().currency_id; + let delegator = Delegator::Substrate( + AccountId::from_ss58check("YLF9AnL6V1vQRfuiB832NXNGZYCPAWkKLLkh7cf3KwXhB9o").unwrap(), + ); + let amount = 1000u128; + let token_pool = 12000u128; + let vtoken_total_issuance = 10000u128; + + assert_ok!(SlpV2::add_delegator(RuntimeOrigin::root(), STAKING_PROTOCOL, None)); + Currencies::set_balance(VASTR, &AccountId::from([0u8; 32]), vtoken_total_issuance); + assert_ok!(VtokenMinting::increase_token_pool(currency_id, token_pool)); + + set_protocol_configuration(); + + // current relaychain block number 1 < update_interval 100 + last update block number 0 => + // Error + assert_noop!( + SlpV2::update_token_exchange_rate( + RuntimeOrigin::root(), + staking_protocol, + delegator.clone(), + amount + ), + SlpV2Error::::UpdateTokenExchangeRateIntervalTooShort + ); + + RelaychainDataProvider::set_block_number(101); + // current relaychain block number 101 < update_interval 100 + last update block number 0 => + // Ok amount 13 < max_update_amount 12 => Error + assert_noop!( + SlpV2::update_token_exchange_rate( + RuntimeOrigin::root(), + staking_protocol, + delegator.clone(), + amount + ), + SlpV2Error::::UpdateTokenExchangeRateAmountTooLarge + ); + }) +} diff --git a/pallets/slp-v2/src/weights.rs b/pallets/slp-v2/src/weights.rs new file mode 100644 index 000000000..130773e53 --- /dev/null +++ b/pallets/slp-v2/src/weights.rs @@ -0,0 +1,334 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for bifrost_slp_v2 +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-09-04, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `MacBook-Pro-2`, CPU: `` +//! WASM-EXECUTION: Compiled, CHAIN: Some("bifrost-polkadot-local"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/bifrost +// benchmark +// pallet +// --chain=bifrost-polkadot-local +// --steps=50 +// --repeat=20 +// --pallet=bifrost_slp_v2 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --header=./HEADER-GPL3 +// --output=./weight.rs +// --template +// ./weight-template/pallet-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for bifrost_slp_v2. +pub trait WeightInfo { + fn add_delegator() -> Weight; + fn remove_delegator() -> Weight; + fn add_validator() -> Weight; + fn remove_validator() -> Weight; + fn set_protocol_configuration() -> Weight; + fn set_ledger() -> Weight; + fn transfer_to() -> Weight; + fn transfer_back() -> Weight; + fn update_ongoing_time_unit() -> Weight; + fn update_token_exchange_rate() -> Weight; + fn astar_dapp_staking() -> Weight; + fn notify_astar_dapp_staking() -> Weight; +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `SlpV2::NextDelegatorIndexByStakingProtocol` (r:1 w:1) + /// Proof: `SlpV2::NextDelegatorIndexByStakingProtocol` (`max_values`: None, `max_size`: Some(19), added: 2494, mode: `MaxEncodedLen`) + /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) + /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (r:1 w:1) + /// Proof: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (r:1 w:1) + /// Proof: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SlpV2::LedgerByStakingProtocolAndDelegator` (r:0 w:1) + /// Proof: `SlpV2::LedgerByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(252), added: 2727, mode: `MaxEncodedLen`) + fn add_delegator() -> Weight { + // Proof Size summary in bytes: + // Measured: `341` + // Estimated: `3533` + // Minimum execution time: 23_000_000 picoseconds. + Weight::from_parts(24_000_000, 3533) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (r:1 w:1) + /// Proof: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (r:1 w:1) + /// Proof: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::LedgerByStakingProtocolAndDelegator` (r:1 w:1) + /// Proof: `SlpV2::LedgerByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(252), added: 2727, mode: `MaxEncodedLen`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn remove_delegator() -> Weight { + // Proof Size summary in bytes: + // Measured: `548` + // Estimated: `3717` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(22_000_000, 3717) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `SlpV2::ValidatorsByStakingProtocolAndDelegator` (r:1 w:1) + /// Proof: `SlpV2::ValidatorsByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(8772), added: 11247, mode: `MaxEncodedLen`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn add_validator() -> Weight { + // Proof Size summary in bytes: + // Measured: `238` + // Estimated: `12237` + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(13_000_000, 12237) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `SlpV2::ValidatorsByStakingProtocolAndDelegator` (r:1 w:1) + /// Proof: `SlpV2::ValidatorsByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(8772), added: 11247, mode: `MaxEncodedLen`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn remove_validator() -> Weight { + // Proof Size summary in bytes: + // Measured: `353` + // Estimated: `12237` + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 12237) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `SlpV2::ConfigurationByStakingProtocol` (r:1 w:1) + /// Proof: `SlpV2::ConfigurationByStakingProtocol` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_protocol_configuration() -> Weight { + // Proof Size summary in bytes: + // Measured: `238` + // Estimated: `3567` + // Minimum execution time: 10_000_000 picoseconds. + Weight::from_parts(11_000_000, 3567) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (r:1 w:0) + /// Proof: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (r:1 w:0) + /// Proof: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::LedgerByStakingProtocolAndDelegator` (r:1 w:1) + /// Proof: `SlpV2::LedgerByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(252), added: 2727, mode: `MaxEncodedLen`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_ledger() -> Weight { + // Proof Size summary in bytes: + // Measured: `545` + // Estimated: `3717` + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(19_000_000, 3717) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Tokens::Accounts` (r:1 w:0) + /// Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(118), added: 2593, mode: `MaxEncodedLen`) + fn transfer_to() -> Weight { + // Proof Size summary in bytes: + // Measured: `455` + // Estimated: `3583` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 3583) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (r:1 w:0) + /// Proof: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (r:1 w:0) + /// Proof: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::ConfigurationByStakingProtocol` (r:1 w:0) + /// Proof: `SlpV2::ConfigurationByStakingProtocol` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + fn transfer_back() -> Weight { + // Proof Size summary in bytes: + // Measured: `571` + // Estimated: `3567` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(20_000_000, 3567) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + } + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::LastRelayChainBlockNumber` (r:1 w:0) + /// Proof: `ParachainSystem::LastRelayChainBlockNumber` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SlpV2::ConfigurationByStakingProtocol` (r:1 w:0) + /// Proof: `SlpV2::ConfigurationByStakingProtocol` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::LastUpdateOngoingTimeUnitBlockNumber` (r:1 w:1) + /// Proof: `SlpV2::LastUpdateOngoingTimeUnitBlockNumber` (`max_values`: None, `max_size`: Some(21), added: 2496, mode: `MaxEncodedLen`) + /// Storage: `VtokenMinting::OngoingTimeUnit` (r:1 w:1) + /// Proof: `VtokenMinting::OngoingTimeUnit` (`max_values`: None, `max_size`: Some(27), added: 2502, mode: `MaxEncodedLen`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn update_ongoing_time_unit() -> Weight { + // Proof Size summary in bytes: + // Measured: `560` + // Estimated: `3567` + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(19_000_000, 3567) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `SlpV2::ConfigurationByStakingProtocol` (r:1 w:0) + /// Proof: `SlpV2::ConfigurationByStakingProtocol` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `ParachainSystem::ValidationData` (r:1 w:0) + /// Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::LastRelayChainBlockNumber` (r:1 w:0) + /// Proof: `ParachainSystem::LastRelayChainBlockNumber` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SlpV2::LastUpdateTokenExchangeRateBlockNumber` (r:1 w:1) + /// Proof: `SlpV2::LastUpdateTokenExchangeRateBlockNumber` (`max_values`: None, `max_size`: Some(70), added: 2545, mode: `MaxEncodedLen`) + /// Storage: `VtokenMinting::TokenPool` (r:1 w:1) + /// Proof: `VtokenMinting::TokenPool` (`max_values`: None, `max_size`: Some(38), added: 2513, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::LedgerByStakingProtocolAndDelegator` (r:1 w:1) + /// Proof: `SlpV2::LedgerByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(252), added: 2727, mode: `MaxEncodedLen`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn update_token_exchange_rate() -> Weight { + // Proof Size summary in bytes: + // Measured: `755` + // Estimated: `3717` + // Minimum execution time: 25_000_000 picoseconds. + Weight::from_parts(26_000_000, 3717) + .saturating_add(RocksDbWeight::get().reads(10_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (r:1 w:0) + /// Proof: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (r:1 w:0) + /// Proof: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + /// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SlpV2::ConfigurationByStakingProtocol` (r:1 w:0) + /// Proof: `SlpV2::ConfigurationByStakingProtocol` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SlpV2::PendingStatusByQueryId` (r:0 w:1) + /// Proof: `SlpV2::PendingStatusByQueryId` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Storage: `PolkadotXcm::Queries` (r:0 w:1) + /// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn astar_dapp_staking() -> Weight { + // Proof Size summary in bytes: + // Measured: `836` + // Estimated: `3567` + // Minimum execution time: 30_000_000 picoseconds. + Weight::from_parts(31_000_000, 3567) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `SlpV2::PendingStatusByQueryId` (r:1 w:0) + /// Proof: `SlpV2::PendingStatusByQueryId` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + /// Storage: `SlpV2::LedgerByStakingProtocolAndDelegator` (r:1 w:1) + /// Proof: `SlpV2::LedgerByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(252), added: 2727, mode: `MaxEncodedLen`) + /// Storage: `System::Number` (r:1 w:0) + /// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::ExecutionPhase` (r:1 w:0) + /// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + /// Storage: `System::EventCount` (r:1 w:1) + /// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Events` (r:1 w:1) + /// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn notify_astar_dapp_staking() -> Weight { + // Proof Size summary in bytes: + // Measured: `567` + // Estimated: `3717` + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(16_000_000, 3717) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } +} diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 7e276ed5e..f7ecb6b25 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -19,6 +19,7 @@ xcm-executor = { workspace = true } zenlink-protocol = { workspace = true } orml-oracle = { workspace = true } +orml-traits = { workspace = true } [features] default = ["std"] @@ -36,6 +37,7 @@ std = [ "zenlink-protocol/std", "orml-oracle/std", + "orml-traits/std", ] with-bifrost-runtime = [ diff --git a/primitives/src/currency.rs b/primitives/src/currency.rs index f3598dff8..066bae3b3 100644 --- a/primitives/src/currency.rs +++ b/primitives/src/currency.rs @@ -55,6 +55,7 @@ pub const DOT_U_TOKEN_ID: u8 = 2u8; pub const DOT_U: CurrencyId = CurrencyId::Token2(DOT_U_TOKEN_ID); pub const ASTR_TOKEN_ID: u8 = 3u8; pub const ASTR: CurrencyId = CurrencyId::Token2(ASTR_TOKEN_ID); +pub const VASTR: CurrencyId = CurrencyId::VToken2(ASTR_TOKEN_ID); pub const FIL_TOKEN_ID: u8 = 4u8; pub const FIL: CurrencyId = CurrencyId::Token2(FIL_TOKEN_ID); pub const VFIL: CurrencyId = CurrencyId::VToken2(FIL_TOKEN_ID); diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 6c3714c4c..c7ff2c6b7 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -20,18 +20,21 @@ #![cfg_attr(not(feature = "std"), no_std)] +use orml_traits::{xcm_transfer::Transferred, XcmTransfer}; use parity_scale_codec::MaxEncodedLen; use scale_info::TypeInfo; use sp_core::{Decode, Encode, RuntimeDebug, H160}; use sp_runtime::{ generic, traits::{BlakeTwo256, IdentifyAccount, Verify}, - FixedU128, MultiSignature, OpaqueExtrinsic, Permill, + DispatchError, FixedU128, MultiSignature, OpaqueExtrinsic, Permill, }; +use sp_std::vec::Vec; use xcm::v4::{prelude::*, Asset, Location}; use xcm_executor::traits::{AssetTransferError, TransferType, XcmAssetTransfers}; pub mod currency; +pub use currency::*; mod salp; pub mod traits; pub use salp::*; @@ -39,13 +42,7 @@ pub use salp::*; #[cfg(test)] mod tests; -pub use crate::{ - currency::{ - AssetIds, CurrencyId, ForeignAssetId, TokenId, TokenSymbol, ASTR, ASTR_TOKEN_ID, BNC, DOT, - DOT_TOKEN_ID, DOT_U, FIL, GLMR, GLMR_TOKEN_ID, KSM, MANTA, VBNC, VDOT, VKSM, VSKSM, - }, - traits::*, -}; +pub use crate::traits::*; /// An index to a block. pub type BlockNumber = u32; @@ -182,6 +179,25 @@ pub enum TimeUnit { Hour(#[codec(compact)] u32), } +impl TimeUnit { + pub fn add_one(self) -> Self { + match self { + TimeUnit::Era(a) => TimeUnit::Era(a.saturating_add(1)), + TimeUnit::SlashingSpan(a) => TimeUnit::SlashingSpan(a.saturating_add(1)), + TimeUnit::Round(a) => TimeUnit::Round(a.saturating_add(1)), + TimeUnit::Kblock(a) => TimeUnit::Kblock(a.saturating_add(1)), + TimeUnit::Hour(a) => TimeUnit::Hour(a.saturating_add(1)), + } + } + + pub fn add(self, other_time: Self) -> Option { + match (self, other_time) { + (TimeUnit::Era(a), TimeUnit::Era(b)) => Some(TimeUnit::Era(a.saturating_add(b))), + _ => None, + } + } +} + impl Default for TimeUnit { fn default() -> Self { TimeUnit::Era(0u32) @@ -263,6 +279,74 @@ impl SendXcm for DoNothingRouter { } } +pub struct MockXcmTransfer; +impl XcmTransfer for MockXcmTransfer { + fn transfer( + who: AccountId, + _currency_id: CurrencyId, + amount: Balance, + dest: Location, + _dest_weight_limit: WeightLimit, + ) -> Result, DispatchError> { + Ok(Transferred { + sender: who, + assets: Default::default(), + fee: Asset { id: AssetId(Location::here()), fun: Fungible(amount) }, + dest, + }) + } + + fn transfer_multiasset( + _who: AccountId, + _asset: Asset, + _dest: Location, + _dest_weight_limit: WeightLimit, + ) -> Result, DispatchError> { + unimplemented!() + } + + fn transfer_with_fee( + _who: AccountId, + _currency_id: CurrencyId, + _amount: Balance, + _fee: Balance, + _dest: Location, + _dest_weight_limit: WeightLimit, + ) -> Result, DispatchError> { + unimplemented!() + } + + fn transfer_multiasset_with_fee( + _who: AccountId, + _asset: Asset, + _fee: Asset, + _dest: Location, + _dest_weight_limit: WeightLimit, + ) -> Result, DispatchError> { + unimplemented!() + } + + fn transfer_multicurrencies( + _who: AccountId, + _currencies: Vec<(CurrencyId, Balance)>, + _fee_item: u32, + _dest: Location, + _dest_weight_limit: WeightLimit, + ) -> Result, DispatchError> { + unimplemented!() + } + + fn transfer_multiassets( + _who: AccountId, + _assets: Assets, + _fee: Asset, + _dest: Location, + _dest_weight_limit: WeightLimit, + ) -> Result, DispatchError> { + unimplemented!() + } +} + pub struct Weightless; impl PreparedMessage for Weightless { fn weight_of(&self) -> Weight { diff --git a/runtime/bifrost-polkadot/Cargo.toml b/runtime/bifrost-polkadot/Cargo.toml index aa5e6fcdf..37c458dda 100644 --- a/runtime/bifrost-polkadot/Cargo.toml +++ b/runtime/bifrost-polkadot/Cargo.toml @@ -150,6 +150,7 @@ bifrost-runtime-common = { workspace = true } bifrost-salp = { workspace = true } bifrost-salp-rpc-runtime-api = { workspace = true } bifrost-slp = { workspace = true } +bifrost-slp-v2 = { workspace = true, features = ["polkadot"] } bifrost-slpx = { workspace = true } bifrost-stable-pool = { workspace = true } bifrost-stable-pool-rpc-runtime-api = { workspace = true } @@ -267,6 +268,7 @@ std = [ "bifrost-salp-rpc-runtime-api/std", "bifrost-salp/std", "bifrost-slp/std", + "bifrost-slp-v2/std", "bifrost-slpx/std", "bifrost-stable-pool-rpc-runtime-api/std", "bifrost-stable-pool/std", @@ -333,6 +335,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "bifrost-slp/runtime-benchmarks", + "bifrost-slp-v2/runtime-benchmarks", "bifrost-salp/runtime-benchmarks", "bifrost-vtoken-minting/runtime-benchmarks", "bifrost-ve-minting/runtime-benchmarks", @@ -359,6 +362,7 @@ try-runtime = [ "bifrost-flexible-fee/try-runtime", "bifrost-salp/try-runtime", "bifrost-slp/try-runtime", + "bifrost-slp-v2/try-runtime", "bifrost-slpx/try-runtime", "bifrost-stable-asset/try-runtime", "bifrost-stable-pool/try-runtime", diff --git a/runtime/bifrost-polkadot/src/lib.rs b/runtime/bifrost-polkadot/src/lib.rs index 47ad953c8..e1e6fb263 100644 --- a/runtime/bifrost-polkadot/src/lib.rs +++ b/runtime/bifrost-polkadot/src/lib.rs @@ -139,6 +139,9 @@ use governance::{ TechAdminOrCouncil, }; +#[cfg(feature = "runtime-benchmarks")] +use bifrost_primitives::{DoNothingRouter, MockXcmTransfer}; + /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades @@ -1605,6 +1608,31 @@ impl bifrost_buy_back::Config for Runtime { type VeMinting = VeMinting; } +impl bifrost_slp_v2::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type ResponseOrigin = EnsureResponse; + type WeightInfo = weights::bifrost_slp_v2::BifrostWeight; + type MultiCurrency = Currencies; + type ControlOrigin = TechAdminOrCouncil; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmTransfer = XTokens; + #[cfg(feature = "runtime-benchmarks")] + type XcmTransfer = MockXcmTransfer; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = DoNothingRouter; + type VtokenMinting = VtokenMinting; + type CurrencyIdConversion = AssetIdMaps; + type RelaychainBlockNumberProvider = RelaychainDataProvider; + type QueryTimeout = QueryTimeout; + type CommissionPalletId = CommissionPalletId; + type ParachainId = ParachainInfo; + type MaxValidators = ConstU32<256>; +} + // Below is the implementation of tokens manipulation functions other than native token. pub struct LocalAssetAdaptor(PhantomData); @@ -1796,6 +1824,7 @@ construct_runtime! { ChannelCommission: bifrost_channel_commission = 136, CloudsConvert: bifrost_clouds_convert = 137, BuyBack: bifrost_buy_back = 138, + SlpV2: bifrost_slp_v2 = 139, } } @@ -1961,6 +1990,7 @@ mod benches { define_benchmarks!( [bifrost_ve_minting, VeMinting] [bifrost_buy_back, BuyBack] + [bifrost_slp_v2, SlpV2] ); } diff --git a/runtime/bifrost-polkadot/src/weights/bifrost_slp_v2.rs b/runtime/bifrost-polkadot/src/weights/bifrost_slp_v2.rs new file mode 100644 index 000000000..461e29d56 --- /dev/null +++ b/runtime/bifrost-polkadot/src/weights/bifrost_slp_v2.rs @@ -0,0 +1,319 @@ +// This file is part of Bifrost. + +// Copyright (C) Liebi Technologies PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for bifrost_slp_v2 +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-09-04, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `MacBook-Pro-2`, CPU: `` +//! WASM-EXECUTION: Compiled, CHAIN: Some("bifrost-polkadot-local"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/bifrost +// benchmark +// pallet +// --chain=bifrost-polkadot-local +// --steps=50 +// --repeat=20 +// --pallet=bifrost_slp_v2 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --header=./HEADER-GPL3 +// --output=./weight.rs +// --template +// ./weight-template/runtime-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions for bifrost_slp_v2. +pub struct BifrostWeight(PhantomData); +impl bifrost_slp_v2::WeightInfo for BifrostWeight { + // Storage: `SlpV2::NextDelegatorIndexByStakingProtocol` (r:1 w:1) + // Proof: `SlpV2::NextDelegatorIndexByStakingProtocol` (`max_values`: None, `max_size`: Some(19), added: 2494, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (r:1 w:1) + // Proof: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + // Storage: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (r:1 w:1) + // Proof: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + // Storage: `System::Number` (r:1 w:0) + // Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::ExecutionPhase` (r:1 w:0) + // Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + // Storage: `System::EventCount` (r:1 w:1) + // Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::Events` (r:1 w:1) + // Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `SlpV2::LedgerByStakingProtocolAndDelegator` (r:0 w:1) + // Proof: `SlpV2::LedgerByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(252), added: 2727, mode: `MaxEncodedLen`) + fn add_delegator() -> Weight { + // Proof Size summary in bytes: + // Measured: `341` + // Estimated: `3533` + // Minimum execution time: 22_000 nanoseconds. + Weight::from_parts(24_000_000, 3533) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(6)) + } + // Storage: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (r:1 w:1) + // Proof: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + // Storage: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (r:1 w:1) + // Proof: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + // Storage: `SlpV2::LedgerByStakingProtocolAndDelegator` (r:1 w:1) + // Proof: `SlpV2::LedgerByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(252), added: 2727, mode: `MaxEncodedLen`) + // Storage: `System::Number` (r:1 w:0) + // Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::ExecutionPhase` (r:1 w:0) + // Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + // Storage: `System::EventCount` (r:1 w:1) + // Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::Events` (r:1 w:1) + // Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn remove_delegator() -> Weight { + // Proof Size summary in bytes: + // Measured: `548` + // Estimated: `3717` + // Minimum execution time: 20_000 nanoseconds. + Weight::from_parts(21_000_000, 3717) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: `SlpV2::ValidatorsByStakingProtocolAndDelegator` (r:1 w:1) + // Proof: `SlpV2::ValidatorsByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(8772), added: 11247, mode: `MaxEncodedLen`) + // Storage: `System::Number` (r:1 w:0) + // Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::ExecutionPhase` (r:1 w:0) + // Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + // Storage: `System::EventCount` (r:1 w:1) + // Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::Events` (r:1 w:1) + // Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn add_validator() -> Weight { + // Proof Size summary in bytes: + // Measured: `238` + // Estimated: `12237` + // Minimum execution time: 11_000 nanoseconds. + Weight::from_parts(12_000_000, 12237) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: `SlpV2::ValidatorsByStakingProtocolAndDelegator` (r:1 w:1) + // Proof: `SlpV2::ValidatorsByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(8772), added: 11247, mode: `MaxEncodedLen`) + // Storage: `System::Number` (r:1 w:0) + // Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::ExecutionPhase` (r:1 w:0) + // Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + // Storage: `System::EventCount` (r:1 w:1) + // Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::Events` (r:1 w:1) + // Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn remove_validator() -> Weight { + // Proof Size summary in bytes: + // Measured: `353` + // Estimated: `12237` + // Minimum execution time: 13_000 nanoseconds. + Weight::from_parts(13_000_000, 12237) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: `SlpV2::ConfigurationByStakingProtocol` (r:1 w:1) + // Proof: `SlpV2::ConfigurationByStakingProtocol` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + // Storage: `System::Number` (r:1 w:0) + // Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::ExecutionPhase` (r:1 w:0) + // Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + // Storage: `System::EventCount` (r:1 w:1) + // Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::Events` (r:1 w:1) + // Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_protocol_configuration() -> Weight { + // Proof Size summary in bytes: + // Measured: `238` + // Estimated: `3567` + // Minimum execution time: 10_000 nanoseconds. + Weight::from_parts(11_000_000, 3567) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (r:1 w:0) + // Proof: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + // Storage: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (r:1 w:0) + // Proof: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + // Storage: `SlpV2::LedgerByStakingProtocolAndDelegator` (r:1 w:1) + // Proof: `SlpV2::LedgerByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(252), added: 2727, mode: `MaxEncodedLen`) + // Storage: `System::Number` (r:1 w:0) + // Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::ExecutionPhase` (r:1 w:0) + // Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + // Storage: `System::EventCount` (r:1 w:1) + // Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::Events` (r:1 w:1) + // Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_ledger() -> Weight { + // Proof Size summary in bytes: + // Measured: `545` + // Estimated: `3717` + // Minimum execution time: 19_000 nanoseconds. + Weight::from_parts(19_000_000, 3717) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: `Tokens::Accounts` (r:1 w:0) + // Proof: `Tokens::Accounts` (`max_values`: None, `max_size`: Some(118), added: 2593, mode: `MaxEncodedLen`) + fn transfer_to() -> Weight { + // Proof Size summary in bytes: + // Measured: `455` + // Estimated: `3583` + // Minimum execution time: 9_000 nanoseconds. + Weight::from_parts(9_000_000, 3583) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (r:1 w:0) + // Proof: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + // Storage: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (r:1 w:0) + // Proof: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + // Storage: `SlpV2::ConfigurationByStakingProtocol` (r:1 w:0) + // Proof: `SlpV2::ConfigurationByStakingProtocol` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + fn transfer_back() -> Weight { + // Proof Size summary in bytes: + // Measured: `571` + // Estimated: `3567` + // Minimum execution time: 19_000 nanoseconds. + Weight::from_parts(20_000_000, 3567) + .saturating_add(T::DbWeight::get().reads(3)) + } + // Storage: `ParachainSystem::ValidationData` (r:1 w:0) + // Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::LastRelayChainBlockNumber` (r:1 w:0) + // Proof: `ParachainSystem::LastRelayChainBlockNumber` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `SlpV2::ConfigurationByStakingProtocol` (r:1 w:0) + // Proof: `SlpV2::ConfigurationByStakingProtocol` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + // Storage: `SlpV2::LastUpdateOngoingTimeUnitBlockNumber` (r:1 w:1) + // Proof: `SlpV2::LastUpdateOngoingTimeUnitBlockNumber` (`max_values`: None, `max_size`: Some(21), added: 2496, mode: `MaxEncodedLen`) + // Storage: `VtokenMinting::OngoingTimeUnit` (r:1 w:1) + // Proof: `VtokenMinting::OngoingTimeUnit` (`max_values`: None, `max_size`: Some(27), added: 2502, mode: `MaxEncodedLen`) + // Storage: `System::Number` (r:1 w:0) + // Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::ExecutionPhase` (r:1 w:0) + // Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + // Storage: `System::EventCount` (r:1 w:1) + // Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::Events` (r:1 w:1) + // Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn update_ongoing_time_unit() -> Weight { + // Proof Size summary in bytes: + // Measured: `560` + // Estimated: `3567` + // Minimum execution time: 19_000 nanoseconds. + Weight::from_parts(19_000_000, 3567) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } + // Storage: `SlpV2::ConfigurationByStakingProtocol` (r:1 w:0) + // Proof: `SlpV2::ConfigurationByStakingProtocol` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::ValidationData` (r:1 w:0) + // Proof: `ParachainSystem::ValidationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::LastRelayChainBlockNumber` (r:1 w:0) + // Proof: `ParachainSystem::LastRelayChainBlockNumber` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `SlpV2::LastUpdateTokenExchangeRateBlockNumber` (r:1 w:1) + // Proof: `SlpV2::LastUpdateTokenExchangeRateBlockNumber` (`max_values`: None, `max_size`: Some(70), added: 2545, mode: `MaxEncodedLen`) + // Storage: `VtokenMinting::TokenPool` (r:1 w:1) + // Proof: `VtokenMinting::TokenPool` (`max_values`: None, `max_size`: Some(38), added: 2513, mode: `MaxEncodedLen`) + // Storage: `SlpV2::LedgerByStakingProtocolAndDelegator` (r:1 w:1) + // Proof: `SlpV2::LedgerByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(252), added: 2727, mode: `MaxEncodedLen`) + // Storage: `System::Number` (r:1 w:0) + // Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::ExecutionPhase` (r:1 w:0) + // Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + // Storage: `System::EventCount` (r:1 w:1) + // Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::Events` (r:1 w:1) + // Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn update_token_exchange_rate() -> Weight { + // Proof Size summary in bytes: + // Measured: `755` + // Estimated: `3717` + // Minimum execution time: 25_000 nanoseconds. + Weight::from_parts(26_000_000, 3717) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (r:1 w:0) + // Proof: `SlpV2::DelegatorIndexByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + // Storage: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (r:1 w:0) + // Proof: `SlpV2::DelegatorByStakingProtocolAndDelegatorIndex` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + // Storage: `System::Number` (r:1 w:0) + // Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::QueryCounter` (r:1 w:1) + // Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `SlpV2::ConfigurationByStakingProtocol` (r:1 w:0) + // Proof: `SlpV2::ConfigurationByStakingProtocol` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + // Storage: `System::ExecutionPhase` (r:1 w:0) + // Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + // Storage: `System::EventCount` (r:1 w:1) + // Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::Events` (r:1 w:1) + // Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `SlpV2::PendingStatusByQueryId` (r:0 w:1) + // Proof: `SlpV2::PendingStatusByQueryId` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::Queries` (r:0 w:1) + // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn astar_dapp_staking() -> Weight { + // Proof Size summary in bytes: + // Measured: `836` + // Estimated: `3567` + // Minimum execution time: 30_000 nanoseconds. + Weight::from_parts(31_000_000, 3567) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: `SlpV2::PendingStatusByQueryId` (r:1 w:0) + // Proof: `SlpV2::PendingStatusByQueryId` (`max_values`: None, `max_size`: Some(75), added: 2550, mode: `MaxEncodedLen`) + // Storage: `SlpV2::LedgerByStakingProtocolAndDelegator` (r:1 w:1) + // Proof: `SlpV2::LedgerByStakingProtocolAndDelegator` (`max_values`: None, `max_size`: Some(252), added: 2727, mode: `MaxEncodedLen`) + // Storage: `System::Number` (r:1 w:0) + // Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::ExecutionPhase` (r:1 w:0) + // Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`) + // Storage: `System::EventCount` (r:1 w:1) + // Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `System::Events` (r:1 w:1) + // Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn notify_astar_dapp_staking() -> Weight { + // Proof Size summary in bytes: + // Measured: `567` + // Estimated: `3717` + // Minimum execution time: 15_000 nanoseconds. + Weight::from_parts(16_000_000, 3717) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(3)) + } +} diff --git a/runtime/bifrost-polkadot/src/weights/mod.rs b/runtime/bifrost-polkadot/src/weights/mod.rs index 2dfce402e..2297c92cc 100644 --- a/runtime/bifrost-polkadot/src/weights/mod.rs +++ b/runtime/bifrost-polkadot/src/weights/mod.rs @@ -31,6 +31,7 @@ pub mod bifrost_fee_share; pub mod bifrost_flexible_fee; pub mod bifrost_salp; pub mod bifrost_slp; +pub mod bifrost_slp_v2; pub mod bifrost_slpx; pub mod bifrost_stable_pool; pub mod bifrost_system_maker;