mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 20:21:03 +00:00
Adds Snowbridge to Rococo runtime (#2522)
# Description Adds Snowbridge to the Rococo bridge hub runtime. Includes config changes required in Rococo asset hub. --------- Co-authored-by: Alistair Singh <alistair.singh7@gmail.com> Co-authored-by: ron <yrong1997@gmail.com> Co-authored-by: Vincent Geddes <vincent.geddes@hey.com> Co-authored-by: claravanstaden <Cats 4 life!>
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
//! Helpers for implementing runtime api
|
||||
|
||||
use snowbridge_core::AgentId;
|
||||
use xcm::{prelude::*, VersionedMultiLocation};
|
||||
|
||||
use crate::{agent_id_of, Config};
|
||||
|
||||
pub fn agent_id<Runtime>(location: VersionedMultiLocation) -> Option<AgentId>
|
||||
where
|
||||
Runtime: Config,
|
||||
{
|
||||
let location: MultiLocation = location.try_into().ok()?;
|
||||
agent_id_of::<Runtime>(&location).ok()
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
//! Benchmarking setup for pallet-template
|
||||
use super::*;
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::Pallet as SnowbridgeControl;
|
||||
use frame_benchmarking::v2::*;
|
||||
use frame_system::RawOrigin;
|
||||
use snowbridge_core::{eth, outbound::OperatingMode};
|
||||
use sp_runtime::SaturatedConversion;
|
||||
use xcm::prelude::*;
|
||||
|
||||
#[allow(clippy::result_large_err)]
|
||||
fn fund_sovereign_account<T: Config>(para_id: ParaId) -> Result<(), BenchmarkError> {
|
||||
let amount: BalanceOf<T> = (10_000_000_000_000_u64).saturated_into::<u128>().saturated_into();
|
||||
let sovereign_account = sibling_sovereign_account::<T>(para_id);
|
||||
T::Token::mint_into(&sovereign_account, amount)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn upgrade() -> Result<(), BenchmarkError> {
|
||||
let impl_address = H160::repeat_byte(1);
|
||||
let impl_code_hash = H256::repeat_byte(1);
|
||||
|
||||
// Assume 256 bytes passed to initializer
|
||||
let params: Vec<u8> = (0..256).map(|_| 1u8).collect();
|
||||
|
||||
#[extrinsic_call]
|
||||
_(
|
||||
RawOrigin::Root,
|
||||
impl_address,
|
||||
impl_code_hash,
|
||||
Some(Initializer { params, maximum_required_gas: 100000 }),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn set_operating_mode() -> Result<(), BenchmarkError> {
|
||||
#[extrinsic_call]
|
||||
_(RawOrigin::Root, OperatingMode::RejectingOutboundMessages);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn set_pricing_parameters() -> Result<(), BenchmarkError> {
|
||||
let params = T::DefaultPricingParameters::get();
|
||||
|
||||
#[extrinsic_call]
|
||||
_(RawOrigin::Root, params);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn create_agent() -> Result<(), BenchmarkError> {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let origin = T::Helper::make_xcm_origin(origin_location);
|
||||
fund_sovereign_account::<T>(origin_para_id.into())?;
|
||||
|
||||
#[extrinsic_call]
|
||||
_(origin as T::RuntimeOrigin);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn create_channel() -> Result<(), BenchmarkError> {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let origin = T::Helper::make_xcm_origin(origin_location);
|
||||
fund_sovereign_account::<T>(origin_para_id.into())?;
|
||||
|
||||
SnowbridgeControl::<T>::create_agent(origin.clone())?;
|
||||
|
||||
#[extrinsic_call]
|
||||
_(origin as T::RuntimeOrigin, OperatingMode::Normal);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn update_channel() -> Result<(), BenchmarkError> {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let origin = T::Helper::make_xcm_origin(origin_location);
|
||||
fund_sovereign_account::<T>(origin_para_id.into())?;
|
||||
SnowbridgeControl::<T>::create_agent(origin.clone())?;
|
||||
SnowbridgeControl::<T>::create_channel(origin.clone(), OperatingMode::Normal)?;
|
||||
|
||||
#[extrinsic_call]
|
||||
_(origin as T::RuntimeOrigin, OperatingMode::RejectingOutboundMessages);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn force_update_channel() -> Result<(), BenchmarkError> {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let origin = T::Helper::make_xcm_origin(origin_location);
|
||||
let channel_id: ChannelId = ParaId::from(origin_para_id).into();
|
||||
|
||||
fund_sovereign_account::<T>(origin_para_id.into())?;
|
||||
SnowbridgeControl::<T>::create_agent(origin.clone())?;
|
||||
SnowbridgeControl::<T>::create_channel(origin.clone(), OperatingMode::Normal)?;
|
||||
|
||||
#[extrinsic_call]
|
||||
_(RawOrigin::Root, channel_id, OperatingMode::RejectingOutboundMessages);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn transfer_native_from_agent() -> Result<(), BenchmarkError> {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let origin = T::Helper::make_xcm_origin(origin_location);
|
||||
fund_sovereign_account::<T>(origin_para_id.into())?;
|
||||
SnowbridgeControl::<T>::create_agent(origin.clone())?;
|
||||
SnowbridgeControl::<T>::create_channel(origin.clone(), OperatingMode::Normal)?;
|
||||
|
||||
#[extrinsic_call]
|
||||
_(origin as T::RuntimeOrigin, H160::default(), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn force_transfer_native_from_agent() -> Result<(), BenchmarkError> {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let origin = T::Helper::make_xcm_origin(origin_location);
|
||||
fund_sovereign_account::<T>(origin_para_id.into())?;
|
||||
SnowbridgeControl::<T>::create_agent(origin.clone())?;
|
||||
|
||||
let versioned_location: VersionedMultiLocation = origin_location.into();
|
||||
|
||||
#[extrinsic_call]
|
||||
_(RawOrigin::Root, Box::new(versioned_location), H160::default(), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn set_token_transfer_fees() -> Result<(), BenchmarkError> {
|
||||
#[extrinsic_call]
|
||||
_(RawOrigin::Root, 1, 1, eth(1));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(
|
||||
SnowbridgeControl,
|
||||
crate::mock::new_test_ext(true),
|
||||
crate::mock::Test
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,681 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
//! Governance API for controlling the Ethereum side of the bridge
|
||||
//!
|
||||
//! # Extrinsics
|
||||
//!
|
||||
//! ## Agents
|
||||
//!
|
||||
//! Agents are smart contracts on Ethereum that act as proxies for consensus systems on Polkadot
|
||||
//! networks.
|
||||
//!
|
||||
//! * [`Call::create_agent`]: Create agent for a sibling parachain
|
||||
//! * [`Call::transfer_native_from_agent`]: Withdraw ether from an agent
|
||||
//!
|
||||
//! The `create_agent` extrinsic should be called via an XCM `Transact` instruction from the sibling
|
||||
//! parachain.
|
||||
//!
|
||||
//! ## Channels
|
||||
//!
|
||||
//! Each sibling parachain has its own dedicated messaging channel for sending and receiving
|
||||
//! messages. As a prerequisite to creating a channel, the sibling should have already created
|
||||
//! an agent using the `create_agent` extrinsic.
|
||||
//!
|
||||
//! * [`Call::create_channel`]: Create channel for a sibling
|
||||
//! * [`Call::update_channel`]: Update a channel for a sibling
|
||||
//!
|
||||
//! ## Governance
|
||||
//!
|
||||
//! Only Polkadot governance itself can call these extrinsics. Delivery fees are waived.
|
||||
//!
|
||||
//! * [`Call::upgrade`]`: Upgrade the gateway contract
|
||||
//! * [`Call::set_operating_mode`]: Update the operating mode of the gateway contract
|
||||
//! * [`Call::force_update_channel`]: Allow root to update a channel for a sibling
|
||||
//! * [`Call::force_transfer_native_from_agent`]: Allow root to withdraw ether from an agent
|
||||
//!
|
||||
//! Typically, Polkadot governance will use the `force_transfer_native_from_agent` and
|
||||
//! `force_update_channel` and extrinsics to manage agents and channels for system parachains.
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
pub mod migration;
|
||||
|
||||
pub mod api;
|
||||
pub mod weights;
|
||||
pub use weights::*;
|
||||
|
||||
use frame_support::{
|
||||
pallet_prelude::*,
|
||||
traits::{
|
||||
fungible::{Inspect, Mutate},
|
||||
tokens::Preservation,
|
||||
Contains, EnsureOrigin,
|
||||
},
|
||||
};
|
||||
use frame_system::pallet_prelude::*;
|
||||
use snowbridge_core::{
|
||||
meth,
|
||||
outbound::{Command, Initializer, Message, OperatingMode, SendError, SendMessage},
|
||||
sibling_sovereign_account, AgentId, Channel, ChannelId, ParaId,
|
||||
PricingParameters as PricingParametersRecord, PRIMARY_GOVERNANCE_CHANNEL,
|
||||
SECONDARY_GOVERNANCE_CHANNEL,
|
||||
};
|
||||
use sp_core::{RuntimeDebug, H160, H256};
|
||||
use sp_io::hashing::blake2_256;
|
||||
use sp_runtime::{traits::BadOrigin, DispatchError, SaturatedConversion};
|
||||
use sp_std::prelude::*;
|
||||
use xcm::prelude::*;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
use frame_support::traits::OriginTrait;
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
pub type BalanceOf<T> =
|
||||
<<T as pallet::Config>::Token as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
|
||||
pub type PricingParametersOf<T> = PricingParametersRecord<BalanceOf<T>>;
|
||||
|
||||
/// Ensure origin location is a sibling
|
||||
fn ensure_sibling<T>(location: &MultiLocation) -> Result<(ParaId, H256), DispatchError>
|
||||
where
|
||||
T: Config,
|
||||
{
|
||||
match location {
|
||||
MultiLocation { parents: 1, interior: X1(Parachain(para_id)) } => {
|
||||
let agent_id = agent_id_of::<T>(location)?;
|
||||
Ok(((*para_id).into(), agent_id))
|
||||
},
|
||||
_ => Err(BadOrigin.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash the location to produce an agent id
|
||||
fn agent_id_of<T: Config>(location: &MultiLocation) -> Result<H256, DispatchError> {
|
||||
T::AgentIdOf::convert_location(location).ok_or(Error::<T>::LocationConversionFailed.into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub trait BenchmarkHelper<O>
|
||||
where
|
||||
O: OriginTrait,
|
||||
{
|
||||
fn make_xcm_origin(location: MultiLocation) -> O;
|
||||
}
|
||||
|
||||
/// Whether a fee should be withdrawn to an account for sending an outbound message
|
||||
#[derive(Clone, PartialEq, RuntimeDebug)]
|
||||
pub enum PaysFee<T>
|
||||
where
|
||||
T: Config,
|
||||
{
|
||||
/// Fully charge includes (local + remote fee)
|
||||
Yes(AccountIdOf<T>),
|
||||
/// Partially charge includes local fee only
|
||||
Partial(AccountIdOf<T>),
|
||||
/// No charge
|
||||
No,
|
||||
}
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use snowbridge_core::StaticLookup;
|
||||
use sp_core::U256;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
|
||||
/// Send messages to Ethereum
|
||||
type OutboundQueue: SendMessage<Balance = BalanceOf<Self>>;
|
||||
|
||||
/// Origin check for XCM locations that can create agents
|
||||
type SiblingOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = MultiLocation>;
|
||||
|
||||
/// Converts MultiLocation to AgentId
|
||||
type AgentIdOf: ConvertLocation<AgentId>;
|
||||
|
||||
/// Token reserved for control operations
|
||||
type Token: Mutate<Self::AccountId>;
|
||||
|
||||
/// TreasuryAccount to collect fees
|
||||
#[pallet::constant]
|
||||
type TreasuryAccount: Get<Self::AccountId>;
|
||||
|
||||
/// Number of decimal places of local currency
|
||||
type DefaultPricingParameters: Get<PricingParametersOf<Self>>;
|
||||
|
||||
/// Cost of delivering a message from Ethereum
|
||||
type InboundDeliveryCost: Get<BalanceOf<Self>>;
|
||||
|
||||
type WeightInfo: WeightInfo;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type Helper: BenchmarkHelper<Self::RuntimeOrigin>;
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// An Upgrade message was sent to the Gateway
|
||||
Upgrade {
|
||||
impl_address: H160,
|
||||
impl_code_hash: H256,
|
||||
initializer_params_hash: Option<H256>,
|
||||
},
|
||||
/// An CreateAgent message was sent to the Gateway
|
||||
CreateAgent {
|
||||
location: Box<MultiLocation>,
|
||||
agent_id: AgentId,
|
||||
},
|
||||
/// An CreateChannel message was sent to the Gateway
|
||||
CreateChannel {
|
||||
channel_id: ChannelId,
|
||||
agent_id: AgentId,
|
||||
},
|
||||
/// An UpdateChannel message was sent to the Gateway
|
||||
UpdateChannel {
|
||||
channel_id: ChannelId,
|
||||
mode: OperatingMode,
|
||||
},
|
||||
/// An SetOperatingMode message was sent to the Gateway
|
||||
SetOperatingMode {
|
||||
mode: OperatingMode,
|
||||
},
|
||||
/// An TransferNativeFromAgent message was sent to the Gateway
|
||||
TransferNativeFromAgent {
|
||||
agent_id: AgentId,
|
||||
recipient: H160,
|
||||
amount: u128,
|
||||
},
|
||||
/// A SetTokenTransferFees message was sent to the Gateway
|
||||
SetTokenTransferFees {
|
||||
create_asset_xcm: u128,
|
||||
transfer_asset_xcm: u128,
|
||||
register_token: U256,
|
||||
},
|
||||
PricingParametersChanged {
|
||||
params: PricingParametersOf<T>,
|
||||
},
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
LocationConversionFailed,
|
||||
AgentAlreadyCreated,
|
||||
NoAgent,
|
||||
ChannelAlreadyCreated,
|
||||
NoChannel,
|
||||
UnsupportedLocationVersion,
|
||||
InvalidLocation,
|
||||
Send(SendError),
|
||||
InvalidTokenTransferFees,
|
||||
InvalidPricingParameters,
|
||||
}
|
||||
|
||||
/// The set of registered agents
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn agents)]
|
||||
pub type Agents<T: Config> = StorageMap<_, Twox64Concat, AgentId, (), OptionQuery>;
|
||||
|
||||
/// The set of registered channels
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn channels)]
|
||||
pub type Channels<T: Config> = StorageMap<_, Twox64Concat, ChannelId, Channel, OptionQuery>;
|
||||
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn parameters)]
|
||||
pub type PricingParameters<T: Config> =
|
||||
StorageValue<_, PricingParametersOf<T>, ValueQuery, T::DefaultPricingParameters>;
|
||||
|
||||
#[pallet::genesis_config]
|
||||
#[derive(frame_support::DefaultNoBound)]
|
||||
pub struct GenesisConfig<T: Config> {
|
||||
// Own parachain id
|
||||
pub para_id: ParaId,
|
||||
// AssetHub's parachain id
|
||||
pub asset_hub_para_id: ParaId,
|
||||
#[serde(skip)]
|
||||
pub _config: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[pallet::genesis_build]
|
||||
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
|
||||
fn build(&self) {
|
||||
Pallet::<T>::initialize(self.para_id, self.asset_hub_para_id).expect("infallible; qed");
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Sends command to the Gateway contract to upgrade itself with a new implementation
|
||||
/// contract
|
||||
///
|
||||
/// Fee required: No
|
||||
///
|
||||
/// - `origin`: Must be `Root`.
|
||||
/// - `impl_address`: The address of the implementation contract.
|
||||
/// - `impl_code_hash`: The codehash of the implementation contract.
|
||||
/// - `initializer`: Optionally call an initializer on the implementation contract.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight((T::WeightInfo::upgrade(), DispatchClass::Operational))]
|
||||
pub fn upgrade(
|
||||
origin: OriginFor<T>,
|
||||
impl_address: H160,
|
||||
impl_code_hash: H256,
|
||||
initializer: Option<Initializer>,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
|
||||
let initializer_params_hash: Option<H256> =
|
||||
initializer.as_ref().map(|i| H256::from(blake2_256(i.params.as_ref())));
|
||||
let command = Command::Upgrade { impl_address, impl_code_hash, initializer };
|
||||
Self::send(PRIMARY_GOVERNANCE_CHANNEL, command, PaysFee::<T>::No)?;
|
||||
|
||||
Self::deposit_event(Event::<T>::Upgrade {
|
||||
impl_address,
|
||||
impl_code_hash,
|
||||
initializer_params_hash,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends a message to the Gateway contract to change its operating mode
|
||||
///
|
||||
/// Fee required: No
|
||||
///
|
||||
/// - `origin`: Must be `MultiLocation`
|
||||
#[pallet::call_index(1)]
|
||||
#[pallet::weight((T::WeightInfo::set_operating_mode(), DispatchClass::Operational))]
|
||||
pub fn set_operating_mode(origin: OriginFor<T>, mode: OperatingMode) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
|
||||
let command = Command::SetOperatingMode { mode };
|
||||
Self::send(PRIMARY_GOVERNANCE_CHANNEL, command, PaysFee::<T>::No)?;
|
||||
|
||||
Self::deposit_event(Event::<T>::SetOperatingMode { mode });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set pricing parameters on both sides of the bridge
|
||||
///
|
||||
/// Fee required: No
|
||||
///
|
||||
/// - `origin`: Must be root
|
||||
#[pallet::call_index(2)]
|
||||
#[pallet::weight((T::WeightInfo::set_pricing_parameters(), DispatchClass::Operational))]
|
||||
pub fn set_pricing_parameters(
|
||||
origin: OriginFor<T>,
|
||||
params: PricingParametersOf<T>,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
params.validate().map_err(|_| Error::<T>::InvalidPricingParameters)?;
|
||||
PricingParameters::<T>::put(params.clone());
|
||||
|
||||
let command = Command::SetPricingParameters {
|
||||
exchange_rate: params.exchange_rate.into(),
|
||||
delivery_cost: T::InboundDeliveryCost::get().saturated_into::<u128>(),
|
||||
};
|
||||
Self::send(PRIMARY_GOVERNANCE_CHANNEL, command, PaysFee::<T>::No)?;
|
||||
|
||||
Self::deposit_event(Event::PricingParametersChanged { params });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends a command to the Gateway contract to instantiate a new agent contract representing
|
||||
/// `origin`.
|
||||
///
|
||||
/// Fee required: Yes
|
||||
///
|
||||
/// - `origin`: Must be `MultiLocation` of a sibling parachain
|
||||
#[pallet::call_index(3)]
|
||||
#[pallet::weight(T::WeightInfo::create_agent())]
|
||||
pub fn create_agent(origin: OriginFor<T>) -> DispatchResult {
|
||||
let origin_location: MultiLocation = T::SiblingOrigin::ensure_origin(origin)?;
|
||||
|
||||
// Ensure that origin location is some consensus system on a sibling parachain
|
||||
let (para_id, agent_id) = ensure_sibling::<T>(&origin_location)?;
|
||||
|
||||
// Record the agent id or fail if it has already been created
|
||||
ensure!(!Agents::<T>::contains_key(agent_id), Error::<T>::AgentAlreadyCreated);
|
||||
Agents::<T>::insert(agent_id, ());
|
||||
|
||||
let command = Command::CreateAgent { agent_id };
|
||||
let pays_fee = PaysFee::<T>::Yes(sibling_sovereign_account::<T>(para_id));
|
||||
Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?;
|
||||
|
||||
Self::deposit_event(Event::<T>::CreateAgent {
|
||||
location: Box::new(origin_location),
|
||||
agent_id,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends a message to the Gateway contract to create a new channel representing `origin`
|
||||
///
|
||||
/// Fee required: Yes
|
||||
///
|
||||
/// This extrinsic is permissionless, so a fee is charged to prevent spamming and pay
|
||||
/// for execution costs on the remote side.
|
||||
///
|
||||
/// The message is sent over the bridge on BridgeHub's own channel to the Gateway.
|
||||
///
|
||||
/// - `origin`: Must be `MultiLocation`
|
||||
/// - `mode`: Initial operating mode of the channel
|
||||
#[pallet::call_index(4)]
|
||||
#[pallet::weight(T::WeightInfo::create_channel())]
|
||||
pub fn create_channel(origin: OriginFor<T>, mode: OperatingMode) -> DispatchResult {
|
||||
let origin_location: MultiLocation = T::SiblingOrigin::ensure_origin(origin)?;
|
||||
|
||||
// Ensure that origin location is a sibling parachain
|
||||
let (para_id, agent_id) = ensure_sibling::<T>(&origin_location)?;
|
||||
|
||||
let channel_id: ChannelId = para_id.into();
|
||||
|
||||
ensure!(Agents::<T>::contains_key(agent_id), Error::<T>::NoAgent);
|
||||
ensure!(!Channels::<T>::contains_key(channel_id), Error::<T>::ChannelAlreadyCreated);
|
||||
|
||||
let channel = Channel { agent_id, para_id };
|
||||
Channels::<T>::insert(channel_id, channel);
|
||||
|
||||
let command = Command::CreateChannel { channel_id, agent_id, mode };
|
||||
let pays_fee = PaysFee::<T>::Yes(sibling_sovereign_account::<T>(para_id));
|
||||
Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?;
|
||||
|
||||
Self::deposit_event(Event::<T>::CreateChannel { channel_id, agent_id });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends a message to the Gateway contract to update a channel configuration
|
||||
///
|
||||
/// The origin must already have a channel initialized, as this message is sent over it.
|
||||
///
|
||||
/// A partial fee will be charged for local processing only.
|
||||
///
|
||||
/// - `origin`: Must be `MultiLocation`
|
||||
/// - `mode`: Initial operating mode of the channel
|
||||
#[pallet::call_index(5)]
|
||||
#[pallet::weight(T::WeightInfo::update_channel())]
|
||||
pub fn update_channel(origin: OriginFor<T>, mode: OperatingMode) -> DispatchResult {
|
||||
let origin_location: MultiLocation = T::SiblingOrigin::ensure_origin(origin)?;
|
||||
|
||||
// Ensure that origin location is a sibling parachain
|
||||
let (para_id, _) = ensure_sibling::<T>(&origin_location)?;
|
||||
|
||||
let channel_id: ChannelId = para_id.into();
|
||||
|
||||
ensure!(Channels::<T>::contains_key(channel_id), Error::<T>::NoChannel);
|
||||
|
||||
let command = Command::UpdateChannel { channel_id, mode };
|
||||
let pays_fee = PaysFee::<T>::Partial(sibling_sovereign_account::<T>(para_id));
|
||||
|
||||
// Parachains send the update message on their own channel
|
||||
Self::send(channel_id, command, pays_fee)?;
|
||||
|
||||
Self::deposit_event(Event::<T>::UpdateChannel { channel_id, mode });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends a message to the Gateway contract to update an arbitrary channel
|
||||
///
|
||||
/// Fee required: No
|
||||
///
|
||||
/// - `origin`: Must be root
|
||||
/// - `channel_id`: ID of channel
|
||||
/// - `mode`: Initial operating mode of the channel
|
||||
/// - `outbound_fee`: Fee charged to users for sending outbound messages to Polkadot
|
||||
#[pallet::call_index(6)]
|
||||
#[pallet::weight(T::WeightInfo::force_update_channel())]
|
||||
pub fn force_update_channel(
|
||||
origin: OriginFor<T>,
|
||||
channel_id: ChannelId,
|
||||
mode: OperatingMode,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
|
||||
ensure!(Channels::<T>::contains_key(channel_id), Error::<T>::NoChannel);
|
||||
|
||||
let command = Command::UpdateChannel { channel_id, mode };
|
||||
Self::send(PRIMARY_GOVERNANCE_CHANNEL, command, PaysFee::<T>::No)?;
|
||||
|
||||
Self::deposit_event(Event::<T>::UpdateChannel { channel_id, mode });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends a message to the Gateway contract to transfer ether from an agent to `recipient`.
|
||||
///
|
||||
/// A partial fee will be charged for local processing only.
|
||||
///
|
||||
/// - `origin`: Must be `MultiLocation`
|
||||
#[pallet::call_index(7)]
|
||||
#[pallet::weight(T::WeightInfo::transfer_native_from_agent())]
|
||||
pub fn transfer_native_from_agent(
|
||||
origin: OriginFor<T>,
|
||||
recipient: H160,
|
||||
amount: u128,
|
||||
) -> DispatchResult {
|
||||
let origin_location: MultiLocation = T::SiblingOrigin::ensure_origin(origin)?;
|
||||
|
||||
// Ensure that origin location is some consensus system on a sibling parachain
|
||||
let (para_id, agent_id) = ensure_sibling::<T>(&origin_location)?;
|
||||
|
||||
// Since the origin is also the owner of the channel, they only need to pay
|
||||
// the local processing fee.
|
||||
let pays_fee = PaysFee::<T>::Partial(sibling_sovereign_account::<T>(para_id));
|
||||
|
||||
Self::do_transfer_native_from_agent(
|
||||
agent_id,
|
||||
para_id.into(),
|
||||
recipient,
|
||||
amount,
|
||||
pays_fee,
|
||||
)
|
||||
}
|
||||
|
||||
/// Sends a message to the Gateway contract to transfer ether from an agent to `recipient`.
|
||||
///
|
||||
/// Privileged. Can only be called by root.
|
||||
///
|
||||
/// Fee required: No
|
||||
///
|
||||
/// - `origin`: Must be root
|
||||
/// - `location`: Location used to resolve the agent
|
||||
/// - `recipient`: Recipient of funds
|
||||
/// - `amount`: Amount to transfer
|
||||
#[pallet::call_index(8)]
|
||||
#[pallet::weight(T::WeightInfo::force_transfer_native_from_agent())]
|
||||
pub fn force_transfer_native_from_agent(
|
||||
origin: OriginFor<T>,
|
||||
location: Box<VersionedMultiLocation>,
|
||||
recipient: H160,
|
||||
amount: u128,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
|
||||
// Ensure that location is some consensus system on a sibling parachain
|
||||
let location: MultiLocation =
|
||||
(*location).try_into().map_err(|_| Error::<T>::UnsupportedLocationVersion)?;
|
||||
let (_, agent_id) =
|
||||
ensure_sibling::<T>(&location).map_err(|_| Error::<T>::InvalidLocation)?;
|
||||
|
||||
let pays_fee = PaysFee::<T>::No;
|
||||
|
||||
Self::do_transfer_native_from_agent(
|
||||
agent_id,
|
||||
PRIMARY_GOVERNANCE_CHANNEL,
|
||||
recipient,
|
||||
amount,
|
||||
pays_fee,
|
||||
)
|
||||
}
|
||||
|
||||
/// Sends a message to the Gateway contract to update fee related parameters for
|
||||
/// token transfers.
|
||||
///
|
||||
/// Privileged. Can only be called by root.
|
||||
///
|
||||
/// Fee required: No
|
||||
///
|
||||
/// - `origin`: Must be root
|
||||
/// - `create_asset_xcm`: The XCM execution cost for creating a new asset class on AssetHub,
|
||||
/// in DOT
|
||||
/// - `transfer_asset_xcm`: The XCM execution cost for performing a reserve transfer on
|
||||
/// AssetHub, in DOT
|
||||
/// - `register_token`: The Ether fee for registering a new token, to discourage spamming
|
||||
#[pallet::call_index(9)]
|
||||
#[pallet::weight((T::WeightInfo::set_token_transfer_fees(), DispatchClass::Operational))]
|
||||
pub fn set_token_transfer_fees(
|
||||
origin: OriginFor<T>,
|
||||
create_asset_xcm: u128,
|
||||
transfer_asset_xcm: u128,
|
||||
register_token: U256,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
|
||||
// Basic validation of new costs. Particularly for token registration, we want to ensure
|
||||
// its relatively expensive to discourage spamming. Like at least 100 USD.
|
||||
ensure!(
|
||||
create_asset_xcm > 0 && transfer_asset_xcm > 0 && register_token > meth(100),
|
||||
Error::<T>::InvalidTokenTransferFees
|
||||
);
|
||||
|
||||
let command = Command::SetTokenTransferFees {
|
||||
create_asset_xcm,
|
||||
transfer_asset_xcm,
|
||||
register_token,
|
||||
};
|
||||
Self::send(PRIMARY_GOVERNANCE_CHANNEL, command, PaysFee::<T>::No)?;
|
||||
|
||||
Self::deposit_event(Event::<T>::SetTokenTransferFees {
|
||||
create_asset_xcm,
|
||||
transfer_asset_xcm,
|
||||
register_token,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Send `command` to the Gateway on the Channel identified by `channel_id`
|
||||
fn send(channel_id: ChannelId, command: Command, pays_fee: PaysFee<T>) -> DispatchResult {
|
||||
let message = Message { id: None, channel_id, command };
|
||||
let (ticket, fee) =
|
||||
T::OutboundQueue::validate(&message).map_err(|err| Error::<T>::Send(err))?;
|
||||
|
||||
let payment = match pays_fee {
|
||||
PaysFee::Yes(account) => Some((account, fee.total())),
|
||||
PaysFee::Partial(account) => Some((account, fee.local)),
|
||||
PaysFee::No => None,
|
||||
};
|
||||
|
||||
if let Some((payer, fee)) = payment {
|
||||
T::Token::transfer(
|
||||
&payer,
|
||||
&T::TreasuryAccount::get(),
|
||||
fee,
|
||||
Preservation::Preserve,
|
||||
)?;
|
||||
}
|
||||
|
||||
T::OutboundQueue::deliver(ticket).map_err(|err| Error::<T>::Send(err))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Issue a `Command::TransferNativeFromAgent` command. The command will be sent on the
|
||||
/// channel `channel_id`
|
||||
pub fn do_transfer_native_from_agent(
|
||||
agent_id: H256,
|
||||
channel_id: ChannelId,
|
||||
recipient: H160,
|
||||
amount: u128,
|
||||
pays_fee: PaysFee<T>,
|
||||
) -> DispatchResult {
|
||||
ensure!(Agents::<T>::contains_key(agent_id), Error::<T>::NoAgent);
|
||||
|
||||
let command = Command::TransferNativeFromAgent { agent_id, recipient, amount };
|
||||
Self::send(channel_id, command, pays_fee)?;
|
||||
|
||||
Self::deposit_event(Event::<T>::TransferNativeFromAgent {
|
||||
agent_id,
|
||||
recipient,
|
||||
amount,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initializes agents and channels.
|
||||
pub fn initialize(para_id: ParaId, asset_hub_para_id: ParaId) -> Result<(), DispatchError> {
|
||||
// Asset Hub
|
||||
let asset_hub_location: MultiLocation =
|
||||
ParentThen(X1(Parachain(asset_hub_para_id.into()))).into();
|
||||
let asset_hub_agent_id = agent_id_of::<T>(&asset_hub_location)?;
|
||||
let asset_hub_channel_id: ChannelId = asset_hub_para_id.into();
|
||||
Agents::<T>::insert(asset_hub_agent_id, ());
|
||||
Channels::<T>::insert(
|
||||
asset_hub_channel_id,
|
||||
Channel { agent_id: asset_hub_agent_id, para_id: asset_hub_para_id },
|
||||
);
|
||||
|
||||
// Governance channels
|
||||
let bridge_hub_agent_id = agent_id_of::<T>(&MultiLocation::here())?;
|
||||
// Agent for BridgeHub
|
||||
Agents::<T>::insert(bridge_hub_agent_id, ());
|
||||
|
||||
// Primary governance channel
|
||||
Channels::<T>::insert(
|
||||
PRIMARY_GOVERNANCE_CHANNEL,
|
||||
Channel { agent_id: bridge_hub_agent_id, para_id },
|
||||
);
|
||||
|
||||
// Secondary governance channel
|
||||
Channels::<T>::insert(
|
||||
SECONDARY_GOVERNANCE_CHANNEL,
|
||||
Channel { agent_id: bridge_hub_agent_id, para_id },
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks if the pallet has been initialized.
|
||||
pub(crate) fn is_initialized() -> bool {
|
||||
let primary_exists = Channels::<T>::contains_key(PRIMARY_GOVERNANCE_CHANNEL);
|
||||
let secondary_exists = Channels::<T>::contains_key(SECONDARY_GOVERNANCE_CHANNEL);
|
||||
primary_exists && secondary_exists
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> StaticLookup for Pallet<T> {
|
||||
type Source = ChannelId;
|
||||
type Target = Channel;
|
||||
fn lookup(channel_id: Self::Source) -> Option<Self::Target> {
|
||||
Channels::<T>::get(channel_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Contains<ChannelId> for Pallet<T> {
|
||||
fn contains(channel_id: &ChannelId) -> bool {
|
||||
Channels::<T>::get(channel_id).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Get<PricingParametersOf<T>> for Pallet<T> {
|
||||
fn get() -> PricingParametersOf<T> {
|
||||
PricingParameters::<T>::get()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
//! Governance API for controlling the Ethereum side of the bridge
|
||||
use super::*;
|
||||
use frame_support::traits::OnRuntimeUpgrade;
|
||||
use log;
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
use sp_runtime::TryRuntimeError;
|
||||
|
||||
pub mod v0 {
|
||||
use frame_support::{pallet_prelude::*, weights::Weight};
|
||||
|
||||
use super::*;
|
||||
|
||||
const LOG_TARGET: &str = "ethereum_system::migration";
|
||||
|
||||
pub struct InitializeOnUpgrade<T, BridgeHubParaId, AssetHubParaId>(
|
||||
sp_std::marker::PhantomData<(T, BridgeHubParaId, AssetHubParaId)>,
|
||||
);
|
||||
impl<T, BridgeHubParaId, AssetHubParaId> OnRuntimeUpgrade
|
||||
for InitializeOnUpgrade<T, BridgeHubParaId, AssetHubParaId>
|
||||
where
|
||||
T: Config,
|
||||
BridgeHubParaId: Get<u32>,
|
||||
AssetHubParaId: Get<u32>,
|
||||
{
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
if !Pallet::<T>::is_initialized() {
|
||||
Pallet::<T>::initialize(
|
||||
BridgeHubParaId::get().into(),
|
||||
AssetHubParaId::get().into(),
|
||||
)
|
||||
.expect("infallible; qed");
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"Ethereum system initialized."
|
||||
);
|
||||
T::DbWeight::get().reads_writes(2, 5)
|
||||
} else {
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"Ethereum system already initialized. Skipping."
|
||||
);
|
||||
T::DbWeight::get().reads(2)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
||||
if !Pallet::<T>::is_initialized() {
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"Agents and channels not initialized. Initialization will run."
|
||||
);
|
||||
} else {
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"Agents and channels are initialized. Initialization will not run."
|
||||
);
|
||||
}
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
|
||||
frame_support::ensure!(
|
||||
Pallet::<T>::is_initialized(),
|
||||
"Agents and channels were not initialized."
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use crate as snowbridge_system;
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
traits::{tokens::fungible::Mutate, ConstU128, ConstU16, ConstU64, ConstU8},
|
||||
weights::IdentityFee,
|
||||
PalletId,
|
||||
};
|
||||
use sp_core::H256;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
use snowbridge_core::{
|
||||
gwei, meth, outbound::ConstantGasMeter, sibling_sovereign_account, AgentId, AllowSiblingsOnly,
|
||||
ParaId, PricingParameters, Rewards,
|
||||
};
|
||||
use sp_runtime::{
|
||||
traits::{AccountIdConversion, BlakeTwo256, IdentityLookup, Keccak256},
|
||||
AccountId32, BuildStorage, FixedU128,
|
||||
};
|
||||
use xcm::prelude::*;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
use crate::BenchmarkHelper;
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
type Balance = u128;
|
||||
|
||||
pub type AccountId = AccountId32;
|
||||
|
||||
// A stripped-down version of pallet-xcm that only inserts an XCM origin into the runtime
|
||||
#[allow(dead_code)]
|
||||
#[frame_support::pallet]
|
||||
mod pallet_xcm_origin {
|
||||
use frame_support::{
|
||||
pallet_prelude::*,
|
||||
traits::{Contains, OriginTrait},
|
||||
};
|
||||
use xcm::latest::prelude::*;
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
type RuntimeOrigin: From<Origin> + From<<Self as frame_system::Config>::RuntimeOrigin>;
|
||||
}
|
||||
|
||||
// Insert this custom Origin into the aggregate RuntimeOrigin
|
||||
#[pallet::origin]
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
|
||||
pub struct Origin(pub MultiLocation);
|
||||
|
||||
impl From<MultiLocation> for Origin {
|
||||
fn from(location: MultiLocation) -> Origin {
|
||||
Origin(location)
|
||||
}
|
||||
}
|
||||
|
||||
/// `EnsureOrigin` implementation succeeding with a `MultiLocation` value to recognize and
|
||||
/// filter the contained location
|
||||
pub struct EnsureXcm<F>(PhantomData<F>);
|
||||
impl<O: OriginTrait + From<Origin>, F: Contains<MultiLocation>> EnsureOrigin<O> for EnsureXcm<F>
|
||||
where
|
||||
O::PalletsOrigin: From<Origin> + TryInto<Origin, Error = O::PalletsOrigin>,
|
||||
{
|
||||
type Success = MultiLocation;
|
||||
|
||||
fn try_origin(outer: O) -> Result<Self::Success, O> {
|
||||
outer.try_with_caller(|caller| {
|
||||
caller.try_into().and_then(|o| match o {
|
||||
Origin(location) if F::contains(&location) => Ok(location),
|
||||
o => Err(o.into()),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn try_successful_origin() -> Result<O, ()> {
|
||||
Ok(O::from(Origin(MultiLocation { parents: 1, interior: X1(Parachain(2000)) })))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure a mock runtime to test the pallet.
|
||||
frame_support::construct_runtime!(
|
||||
pub enum Test
|
||||
{
|
||||
System: frame_system,
|
||||
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
|
||||
XcmOrigin: pallet_xcm_origin::{Pallet, Origin},
|
||||
OutboundQueue: snowbridge_outbound_queue::{Pallet, Call, Storage, Event<T>},
|
||||
EthereumSystem: snowbridge_system,
|
||||
MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event<T>}
|
||||
}
|
||||
);
|
||||
|
||||
impl frame_system::Config for Test {
|
||||
type BaseCallFilter = frame_support::traits::Everything;
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = ();
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type RuntimeTask = RuntimeTask;
|
||||
type Hash = H256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = AccountId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type BlockHashCount = ConstU64<250>;
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = pallet_balances::AccountData<u128>;
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type SS58Prefix = ConstU16<42>;
|
||||
type OnSetCode = ();
|
||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
type Nonce = u64;
|
||||
type Block = Block;
|
||||
}
|
||||
|
||||
impl pallet_balances::Config for Test {
|
||||
type MaxLocks = ();
|
||||
type MaxReserves = ();
|
||||
type ReserveIdentifier = [u8; 8];
|
||||
type Balance = Balance;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ConstU128<1>;
|
||||
type AccountStore = System;
|
||||
type WeightInfo = ();
|
||||
type FreezeIdentifier = ();
|
||||
type MaxFreezes = ();
|
||||
type RuntimeHoldReason = ();
|
||||
type RuntimeFreezeReason = ();
|
||||
type MaxHolds = ();
|
||||
}
|
||||
|
||||
impl pallet_xcm_origin::Config for Test {
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const HeapSize: u32 = 32 * 1024;
|
||||
pub const MaxStale: u32 = 32;
|
||||
pub static ServiceWeight: Option<Weight> = Some(Weight::from_parts(100, 100));
|
||||
}
|
||||
|
||||
impl pallet_message_queue::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = ();
|
||||
type MessageProcessor = OutboundQueue;
|
||||
type Size = u32;
|
||||
type QueueChangeHandler = ();
|
||||
type HeapSize = HeapSize;
|
||||
type MaxStale = MaxStale;
|
||||
type ServiceWeight = ServiceWeight;
|
||||
type QueuePausedQuery = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const MaxMessagePayloadSize: u32 = 1024;
|
||||
pub const MaxMessagesPerBlock: u32 = 20;
|
||||
pub const OwnParaId: ParaId = ParaId::new(1013);
|
||||
}
|
||||
|
||||
impl snowbridge_outbound_queue::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Hashing = Keccak256;
|
||||
type MessageQueue = MessageQueue;
|
||||
type Decimals = ConstU8<10>;
|
||||
type MaxMessagePayloadSize = MaxMessagePayloadSize;
|
||||
type MaxMessagesPerBlock = MaxMessagesPerBlock;
|
||||
type GasMeter = ConstantGasMeter;
|
||||
type Balance = u128;
|
||||
type PricingParameters = EthereumSystem;
|
||||
type Channels = EthereumSystem;
|
||||
type WeightToFee = IdentityFee<u128>;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const SS58Prefix: u8 = 42;
|
||||
pub const AnyNetwork: Option<NetworkId> = None;
|
||||
pub const RelayNetwork: Option<NetworkId> = Some(NetworkId::Kusama);
|
||||
pub const RelayLocation: MultiLocation = MultiLocation::parent();
|
||||
pub UniversalLocation: InteriorMultiLocation =
|
||||
X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013));
|
||||
}
|
||||
|
||||
pub const DOT: u128 = 10_000_000_000;
|
||||
|
||||
parameter_types! {
|
||||
pub TreasuryAccount: AccountId = PalletId(*b"py/trsry").into_account_truncating();
|
||||
pub Fee: u64 = 1000;
|
||||
pub const RococoNetwork: NetworkId = NetworkId::Rococo;
|
||||
pub const InitialFunding: u128 = 1_000_000_000_000;
|
||||
pub AssetHubParaId: ParaId = ParaId::new(1000);
|
||||
pub TestParaId: u32 = 2000;
|
||||
pub Parameters: PricingParameters<u128> = PricingParameters {
|
||||
exchange_rate: FixedU128::from_rational(1, 400),
|
||||
fee_per_gas: gwei(20),
|
||||
rewards: Rewards { local: DOT, remote: meth(1) }
|
||||
};
|
||||
pub const InboundDeliveryCost: u128 = 1_000_000_000;
|
||||
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
impl BenchmarkHelper<RuntimeOrigin> for () {
|
||||
fn make_xcm_origin(location: MultiLocation) -> RuntimeOrigin {
|
||||
RuntimeOrigin::from(pallet_xcm_origin::Origin(location))
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type OutboundQueue = OutboundQueue;
|
||||
type SiblingOrigin = pallet_xcm_origin::EnsureXcm<AllowSiblingsOnly>;
|
||||
type AgentIdOf = snowbridge_core::AgentIdOf;
|
||||
type TreasuryAccount = TreasuryAccount;
|
||||
type Token = Balances;
|
||||
type DefaultPricingParameters = Parameters;
|
||||
type WeightInfo = ();
|
||||
type InboundDeliveryCost = InboundDeliveryCost;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type Helper = ();
|
||||
}
|
||||
|
||||
// Build genesis storage according to the mock runtime.
|
||||
pub fn new_test_ext(genesis_build: bool) -> sp_io::TestExternalities {
|
||||
let mut storage = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
|
||||
if genesis_build {
|
||||
crate::GenesisConfig::<Test> {
|
||||
para_id: OwnParaId::get(),
|
||||
asset_hub_para_id: AssetHubParaId::get(),
|
||||
_config: Default::default(),
|
||||
}
|
||||
.assimilate_storage(&mut storage)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let mut ext: sp_io::TestExternalities = storage.into();
|
||||
let initial_amount = InitialFunding::get();
|
||||
let test_para_id = TestParaId::get();
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(test_para_id.into());
|
||||
let treasury_account = TreasuryAccount::get();
|
||||
ext.execute_with(|| {
|
||||
System::set_block_number(1);
|
||||
Balances::mint_into(&AccountId32::from([0; 32]), initial_amount).unwrap();
|
||||
Balances::mint_into(&sovereign_account, initial_amount).unwrap();
|
||||
Balances::mint_into(&treasury_account, initial_amount).unwrap();
|
||||
});
|
||||
ext
|
||||
}
|
||||
|
||||
// Test helpers
|
||||
|
||||
pub fn make_xcm_origin(location: MultiLocation) -> RuntimeOrigin {
|
||||
pallet_xcm_origin::Origin(location).into()
|
||||
}
|
||||
|
||||
pub fn make_agent_id(location: MultiLocation) -> AgentId {
|
||||
<Test as snowbridge_system::Config>::AgentIdOf::convert_location(&location)
|
||||
.expect("convert location")
|
||||
}
|
||||
@@ -0,0 +1,664 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use crate::{mock::*, *};
|
||||
use frame_support::{assert_noop, assert_ok};
|
||||
use hex_literal::hex;
|
||||
use snowbridge_core::{eth, sibling_sovereign_account_raw};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{AccountId32, DispatchError::BadOrigin, TokenError};
|
||||
|
||||
#[test]
|
||||
fn create_agent() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let agent_id = make_agent_id(origin_location);
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(origin_para_id.into());
|
||||
|
||||
// fund sovereign account of origin
|
||||
let _ = Balances::mint_into(&sovereign_account, 10000);
|
||||
|
||||
assert!(!Agents::<Test>::contains_key(agent_id));
|
||||
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
assert_ok!(EthereumSystem::create_agent(origin));
|
||||
|
||||
assert!(Agents::<Test>::contains_key(agent_id));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_agent_for_here() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin_location = MultiLocation::here();
|
||||
let agent_id = make_agent_id(origin_location);
|
||||
assert_eq!(
|
||||
agent_id,
|
||||
hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_agent_fails_on_funds_unavailable() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(2000)) };
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
// Reset balance of sovereign_account to zero so to trigger the FundsUnavailable error
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(2000.into());
|
||||
Balances::set_balance(&sovereign_account, 0);
|
||||
assert_noop!(EthereumSystem::create_agent(origin), TokenError::FundsUnavailable);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_agent_bad_origin() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
// relay chain location not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::create_agent(make_xcm_origin(MultiLocation {
|
||||
parents: 1,
|
||||
interior: Here,
|
||||
})),
|
||||
BadOrigin,
|
||||
);
|
||||
|
||||
// local account location not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::create_agent(make_xcm_origin(MultiLocation {
|
||||
parents: 0,
|
||||
interior: X1(Junction::AccountId32 { network: None, id: [67u8; 32] }),
|
||||
})),
|
||||
BadOrigin,
|
||||
);
|
||||
|
||||
// Signed origin not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::create_agent(RuntimeOrigin::signed([14; 32].into())),
|
||||
BadOrigin
|
||||
);
|
||||
|
||||
// None origin not allowed
|
||||
assert_noop!(EthereumSystem::create_agent(RuntimeOrigin::none()), BadOrigin);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_as_root() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::root();
|
||||
let address: H160 = Default::default();
|
||||
let code_hash: H256 = Default::default();
|
||||
|
||||
assert_ok!(EthereumSystem::upgrade(origin, address, code_hash, None));
|
||||
|
||||
System::assert_last_event(RuntimeEvent::EthereumSystem(crate::Event::Upgrade {
|
||||
impl_address: address,
|
||||
impl_code_hash: code_hash,
|
||||
initializer_params_hash: None,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_as_signed_fails() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::signed(AccountId32::new([0; 32]));
|
||||
let address: H160 = Default::default();
|
||||
let code_hash: H256 = Default::default();
|
||||
|
||||
assert_noop!(EthereumSystem::upgrade(origin, address, code_hash, None), BadOrigin);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_with_params() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::root();
|
||||
let address: H160 = Default::default();
|
||||
let code_hash: H256 = Default::default();
|
||||
let initializer: Option<Initializer> =
|
||||
Some(Initializer { params: [0; 256].into(), maximum_required_gas: 10000 });
|
||||
assert_ok!(EthereumSystem::upgrade(origin, address, code_hash, initializer));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_operating_mode() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::root();
|
||||
let mode = OperatingMode::RejectingOutboundMessages;
|
||||
|
||||
assert_ok!(EthereumSystem::set_operating_mode(origin, mode));
|
||||
|
||||
System::assert_last_event(RuntimeEvent::EthereumSystem(crate::Event::SetOperatingMode {
|
||||
mode,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_operating_mode_as_signed_fails() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::signed([14; 32].into());
|
||||
let mode = OperatingMode::RejectingOutboundMessages;
|
||||
|
||||
assert_noop!(EthereumSystem::set_operating_mode(origin, mode), BadOrigin);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_pricing_parameters() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::root();
|
||||
let mut params = Parameters::get();
|
||||
params.rewards.local = 7;
|
||||
|
||||
assert_ok!(EthereumSystem::set_pricing_parameters(origin, params));
|
||||
|
||||
assert_eq!(PricingParameters::<Test>::get().rewards.local, 7);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_pricing_parameters_as_signed_fails() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::signed([14; 32].into());
|
||||
let params = Parameters::get();
|
||||
|
||||
assert_noop!(EthereumSystem::set_pricing_parameters(origin, params), BadOrigin);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_pricing_parameters_invalid() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::root();
|
||||
let mut params = Parameters::get();
|
||||
params.rewards.local = 0;
|
||||
|
||||
assert_noop!(
|
||||
EthereumSystem::set_pricing_parameters(origin.clone(), params),
|
||||
Error::<Test>::InvalidPricingParameters
|
||||
);
|
||||
|
||||
let mut params = Parameters::get();
|
||||
params.exchange_rate = 0u128.into();
|
||||
assert_noop!(
|
||||
EthereumSystem::set_pricing_parameters(origin.clone(), params),
|
||||
Error::<Test>::InvalidPricingParameters
|
||||
);
|
||||
params = Parameters::get();
|
||||
params.fee_per_gas = sp_core::U256::zero();
|
||||
assert_noop!(
|
||||
EthereumSystem::set_pricing_parameters(origin.clone(), params),
|
||||
Error::<Test>::InvalidPricingParameters
|
||||
);
|
||||
params = Parameters::get();
|
||||
params.rewards.local = 0;
|
||||
assert_noop!(
|
||||
EthereumSystem::set_pricing_parameters(origin.clone(), params),
|
||||
Error::<Test>::InvalidPricingParameters
|
||||
);
|
||||
params = Parameters::get();
|
||||
params.rewards.remote = sp_core::U256::zero();
|
||||
assert_noop!(
|
||||
EthereumSystem::set_pricing_parameters(origin, params),
|
||||
Error::<Test>::InvalidPricingParameters
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_token_transfer_fees() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::root();
|
||||
|
||||
assert_ok!(EthereumSystem::set_token_transfer_fees(origin, 1, 1, eth(1)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_token_transfer_fees_root_only() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::signed([14; 32].into());
|
||||
|
||||
assert_noop!(EthereumSystem::set_token_transfer_fees(origin, 1, 1, 1.into()), BadOrigin);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_token_transfer_fees_invalid() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::root();
|
||||
|
||||
assert_noop!(
|
||||
EthereumSystem::set_token_transfer_fees(origin, 0, 0, 0.into()),
|
||||
Error::<Test>::InvalidTokenTransferFees
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_channel() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(origin_para_id.into());
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
|
||||
// fund sovereign account of origin
|
||||
let _ = Balances::mint_into(&sovereign_account, 10000);
|
||||
|
||||
assert_ok!(EthereumSystem::create_agent(origin.clone()));
|
||||
assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_channel_fail_already_exists() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(origin_para_id.into());
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
|
||||
// fund sovereign account of origin
|
||||
let _ = Balances::mint_into(&sovereign_account, 10000);
|
||||
|
||||
assert_ok!(EthereumSystem::create_agent(origin.clone()));
|
||||
assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal));
|
||||
|
||||
assert_noop!(
|
||||
EthereumSystem::create_channel(origin, OperatingMode::Normal),
|
||||
Error::<Test>::ChannelAlreadyCreated
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_channel_bad_origin() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
// relay chain location not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::create_channel(
|
||||
make_xcm_origin(MultiLocation { parents: 1, interior: Here }),
|
||||
OperatingMode::Normal,
|
||||
),
|
||||
BadOrigin,
|
||||
);
|
||||
|
||||
// child of sibling location not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::create_channel(
|
||||
make_xcm_origin(MultiLocation {
|
||||
parents: 1,
|
||||
interior: X2(
|
||||
Parachain(2000),
|
||||
Junction::AccountId32 { network: None, id: [67u8; 32] }
|
||||
),
|
||||
}),
|
||||
OperatingMode::Normal,
|
||||
),
|
||||
BadOrigin,
|
||||
);
|
||||
|
||||
// local account location not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::create_channel(
|
||||
make_xcm_origin(MultiLocation {
|
||||
parents: 0,
|
||||
interior: X1(Junction::AccountId32 { network: None, id: [67u8; 32] }),
|
||||
}),
|
||||
OperatingMode::Normal,
|
||||
),
|
||||
BadOrigin,
|
||||
);
|
||||
|
||||
// Signed origin not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::create_channel(
|
||||
RuntimeOrigin::signed([14; 32].into()),
|
||||
OperatingMode::Normal,
|
||||
),
|
||||
BadOrigin
|
||||
);
|
||||
|
||||
// None origin not allowed
|
||||
assert_noop!(EthereumSystem::create_agent(RuntimeOrigin::none()), BadOrigin);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_channel() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(origin_para_id.into());
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
|
||||
// First create the channel
|
||||
let _ = Balances::mint_into(&sovereign_account, 10000);
|
||||
assert_ok!(EthereumSystem::create_agent(origin.clone()));
|
||||
assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal));
|
||||
|
||||
// Now try to update it
|
||||
assert_ok!(EthereumSystem::update_channel(origin, OperatingMode::Normal));
|
||||
|
||||
System::assert_last_event(RuntimeEvent::EthereumSystem(crate::Event::UpdateChannel {
|
||||
channel_id: ParaId::from(2000).into(),
|
||||
mode: OperatingMode::Normal,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_channel_bad_origin() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let mode = OperatingMode::Normal;
|
||||
|
||||
// relay chain location not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::update_channel(
|
||||
make_xcm_origin(MultiLocation { parents: 1, interior: Here }),
|
||||
mode,
|
||||
),
|
||||
BadOrigin,
|
||||
);
|
||||
|
||||
// child of sibling location not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::update_channel(
|
||||
make_xcm_origin(MultiLocation {
|
||||
parents: 1,
|
||||
interior: X2(
|
||||
Parachain(2000),
|
||||
Junction::AccountId32 { network: None, id: [67u8; 32] }
|
||||
),
|
||||
}),
|
||||
mode,
|
||||
),
|
||||
BadOrigin,
|
||||
);
|
||||
|
||||
// local account location not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::update_channel(
|
||||
make_xcm_origin(MultiLocation {
|
||||
parents: 0,
|
||||
interior: X1(Junction::AccountId32 { network: None, id: [67u8; 32] }),
|
||||
}),
|
||||
mode,
|
||||
),
|
||||
BadOrigin,
|
||||
);
|
||||
|
||||
// Signed origin not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::update_channel(RuntimeOrigin::signed([14; 32].into()), mode),
|
||||
BadOrigin
|
||||
);
|
||||
|
||||
// None origin not allowed
|
||||
assert_noop!(EthereumSystem::update_channel(RuntimeOrigin::none(), mode), BadOrigin);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_channel_fails_not_exist() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(2000)) };
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
|
||||
// Now try to update it
|
||||
assert_noop!(
|
||||
EthereumSystem::update_channel(origin, OperatingMode::Normal),
|
||||
Error::<Test>::NoChannel
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn force_update_channel() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin_para_id = 2000;
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(origin_para_id)) };
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(origin_para_id.into());
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
|
||||
let channel_id: ChannelId = ParaId::from(origin_para_id).into();
|
||||
|
||||
// First create the channel
|
||||
let _ = Balances::mint_into(&sovereign_account, 10000);
|
||||
assert_ok!(EthereumSystem::create_agent(origin.clone()));
|
||||
assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal));
|
||||
|
||||
// Now try to force update it
|
||||
let force_origin = RuntimeOrigin::root();
|
||||
assert_ok!(EthereumSystem::force_update_channel(
|
||||
force_origin,
|
||||
channel_id,
|
||||
OperatingMode::Normal,
|
||||
));
|
||||
|
||||
System::assert_last_event(RuntimeEvent::EthereumSystem(crate::Event::UpdateChannel {
|
||||
channel_id: ParaId::from(2000).into(),
|
||||
mode: OperatingMode::Normal,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn force_update_channel_bad_origin() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let mode = OperatingMode::Normal;
|
||||
|
||||
// signed origin not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::force_update_channel(
|
||||
RuntimeOrigin::signed([14; 32].into()),
|
||||
ParaId::from(1000).into(),
|
||||
mode,
|
||||
),
|
||||
BadOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_native_from_agent() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(2000)) };
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
let recipient: H160 = [27u8; 20].into();
|
||||
let amount = 103435;
|
||||
|
||||
// First create the agent and channel
|
||||
assert_ok!(EthereumSystem::create_agent(origin.clone()));
|
||||
assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal));
|
||||
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
assert_ok!(EthereumSystem::transfer_native_from_agent(origin, recipient, amount),);
|
||||
|
||||
System::assert_last_event(RuntimeEvent::EthereumSystem(
|
||||
crate::Event::TransferNativeFromAgent {
|
||||
agent_id: make_agent_id(origin_location),
|
||||
recipient,
|
||||
amount,
|
||||
},
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn force_transfer_native_from_agent() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let origin = RuntimeOrigin::root();
|
||||
let location = MultiLocation { parents: 1, interior: X1(Parachain(2000)) };
|
||||
let versioned_location: Box<VersionedMultiLocation> = Box::new(location.into());
|
||||
let recipient: H160 = [27u8; 20].into();
|
||||
let amount = 103435;
|
||||
|
||||
// First create the agent
|
||||
Agents::<Test>::insert(make_agent_id(location), ());
|
||||
|
||||
assert_ok!(EthereumSystem::force_transfer_native_from_agent(
|
||||
origin,
|
||||
versioned_location,
|
||||
recipient,
|
||||
amount
|
||||
),);
|
||||
|
||||
System::assert_last_event(RuntimeEvent::EthereumSystem(
|
||||
crate::Event::TransferNativeFromAgent {
|
||||
agent_id: make_agent_id(location),
|
||||
recipient,
|
||||
amount,
|
||||
},
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn force_transfer_native_from_agent_bad_origin() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let recipient: H160 = [27u8; 20].into();
|
||||
let amount = 103435;
|
||||
|
||||
// signed origin not allowed
|
||||
assert_noop!(
|
||||
EthereumSystem::force_transfer_native_from_agent(
|
||||
RuntimeOrigin::signed([14; 32].into()),
|
||||
Box::new(
|
||||
MultiLocation {
|
||||
parents: 1,
|
||||
interior: X2(
|
||||
Parachain(2000),
|
||||
Junction::AccountId32 { network: None, id: [67u8; 32] }
|
||||
),
|
||||
}
|
||||
.into()
|
||||
),
|
||||
recipient,
|
||||
amount,
|
||||
),
|
||||
BadOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: The following tests are not actually tests and are more about obtaining location
|
||||
// conversions for devops purposes. They need to be removed here and incorporated into a command
|
||||
// line utility.
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn check_sibling_sovereign_account() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let para_id = 1001;
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(para_id.into());
|
||||
let sovereign_account_raw = sibling_sovereign_account_raw(para_id.into());
|
||||
println!(
|
||||
"Sovereign account for parachain {}: {:#?}",
|
||||
para_id,
|
||||
hex::encode(sovereign_account.clone())
|
||||
);
|
||||
assert_eq!(sovereign_account, sovereign_account_raw.into());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn charge_fee_for_create_agent() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let para_id: u32 = TestParaId::get();
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(para_id)) };
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(para_id.into());
|
||||
let (_, agent_id) = ensure_sibling::<Test>(&origin_location).unwrap();
|
||||
|
||||
let initial_sovereign_balance = Balances::balance(&sovereign_account);
|
||||
assert_ok!(EthereumSystem::create_agent(origin.clone()));
|
||||
let fee_charged = initial_sovereign_balance - Balances::balance(&sovereign_account);
|
||||
|
||||
assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal));
|
||||
|
||||
// assert sovereign_balance decreased by (fee.base_fee + fee.delivery_fee)
|
||||
let message = Message {
|
||||
id: None,
|
||||
channel_id: ParaId::from(para_id).into(),
|
||||
command: Command::CreateAgent { agent_id },
|
||||
};
|
||||
let (_, fee) = OutboundQueue::validate(&message).unwrap();
|
||||
assert_eq!(fee.local + fee.remote, fee_charged);
|
||||
|
||||
// and treasury_balance increased
|
||||
let treasury_balance = Balances::balance(&TreasuryAccount::get());
|
||||
assert!(treasury_balance > InitialFunding::get());
|
||||
|
||||
let final_sovereign_balance = Balances::balance(&sovereign_account);
|
||||
// (sovereign_balance + treasury_balance) keeps the same
|
||||
assert_eq!(final_sovereign_balance + treasury_balance, { InitialFunding::get() * 2 });
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn charge_fee_for_transfer_native_from_agent() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let para_id: u32 = TestParaId::get();
|
||||
let origin_location = MultiLocation { parents: 1, interior: X1(Parachain(para_id)) };
|
||||
let recipient: H160 = [27u8; 20].into();
|
||||
let amount = 103435;
|
||||
let origin = make_xcm_origin(origin_location);
|
||||
let (_, agent_id) = ensure_sibling::<Test>(&origin_location).unwrap();
|
||||
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(para_id.into());
|
||||
|
||||
// create_agent & create_channel first
|
||||
assert_ok!(EthereumSystem::create_agent(origin.clone()));
|
||||
assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal));
|
||||
|
||||
// assert sovereign_balance decreased by only the base_fee
|
||||
let sovereign_balance_before = Balances::balance(&sovereign_account);
|
||||
assert_ok!(EthereumSystem::transfer_native_from_agent(origin.clone(), recipient, amount));
|
||||
let message = Message {
|
||||
id: None,
|
||||
channel_id: ParaId::from(para_id).into(),
|
||||
command: Command::TransferNativeFromAgent { agent_id, recipient, amount },
|
||||
};
|
||||
let (_, fee) = OutboundQueue::validate(&message).unwrap();
|
||||
let sovereign_balance_after = Balances::balance(&sovereign_account);
|
||||
assert_eq!(sovereign_balance_after + fee.local, sovereign_balance_before);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn charge_fee_for_upgrade() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
let para_id: u32 = TestParaId::get();
|
||||
let origin = RuntimeOrigin::root();
|
||||
let address: H160 = Default::default();
|
||||
let code_hash: H256 = Default::default();
|
||||
let initializer: Option<Initializer> =
|
||||
Some(Initializer { params: [0; 256].into(), maximum_required_gas: 10000 });
|
||||
assert_ok!(EthereumSystem::upgrade(origin, address, code_hash, initializer.clone()));
|
||||
|
||||
// assert sovereign_balance does not change as we do not charge for sudo operations
|
||||
let sovereign_account = sibling_sovereign_account::<Test>(para_id.into());
|
||||
let sovereign_balance = Balances::balance(&sovereign_account);
|
||||
assert_eq!(sovereign_balance, InitialFunding::get());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis_build_initializes_correctly() {
|
||||
new_test_ext(true).execute_with(|| {
|
||||
assert!(EthereumSystem::is_initialized(), "Ethereum uninitialized.");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_genesis_build_is_uninitialized() {
|
||||
new_test_ext(false).execute_with(|| {
|
||||
assert!(!EthereumSystem::is_initialized(), "Ethereum initialized.");
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
|
||||
//! Autogenerated weights for `snowbridge_system`
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2023-10-09, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `crake.local`, CPU: `<UNKNOWN>`
|
||||
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: `1024`
|
||||
|
||||
// Executed Command:
|
||||
// target/release/polkadot-parachain
|
||||
// benchmark
|
||||
// pallet
|
||||
// --chain
|
||||
// bridge-hub-rococo-dev
|
||||
// --pallet=snowbridge_system
|
||||
// --extrinsic=*
|
||||
// --execution=wasm
|
||||
// --wasm-execution=compiled
|
||||
// --template
|
||||
// ../parachain/templates/module-weight-template.hbs
|
||||
// --output
|
||||
// ../parachain/pallets/control/src/weights.rs
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for `snowbridge_system`.
|
||||
pub trait WeightInfo {
|
||||
fn upgrade() -> Weight;
|
||||
fn create_agent() -> Weight;
|
||||
fn create_channel() -> Weight;
|
||||
fn update_channel() -> Weight;
|
||||
fn force_update_channel() -> Weight;
|
||||
fn set_operating_mode() -> Weight;
|
||||
fn transfer_native_from_agent() -> Weight;
|
||||
fn force_transfer_native_from_agent() -> Weight;
|
||||
fn set_token_transfer_fees() -> Weight;
|
||||
fn set_pricing_parameters() -> Weight;
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests.
|
||||
impl WeightInfo for () {
|
||||
/// Storage: ParachainInfo ParachainId (r:1 w:0)
|
||||
/// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
/// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
|
||||
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue ServiceHead (r:1 w:1)
|
||||
/// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue Pages (r:0 w:1)
|
||||
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
|
||||
fn upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `80`
|
||||
// Estimated: `3517`
|
||||
// Minimum execution time: 44_000_000 picoseconds.
|
||||
Weight::from_parts(44_000_000, 3517)
|
||||
.saturating_add(RocksDbWeight::get().reads(4_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
}
|
||||
/// Storage: EthereumSystem Agents (r:1 w:1)
|
||||
/// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen)
|
||||
/// Storage: System Account (r:2 w:2)
|
||||
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, 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: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
|
||||
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue ServiceHead (r:1 w:1)
|
||||
/// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue Pages (r:0 w:1)
|
||||
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
|
||||
fn create_agent() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `187`
|
||||
// Estimated: `6196`
|
||||
// Minimum execution time: 85_000_000 picoseconds.
|
||||
Weight::from_parts(85_000_000, 6196)
|
||||
.saturating_add(RocksDbWeight::get().reads(7_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(6_u64))
|
||||
}
|
||||
/// Storage: System Account (r:2 w:2)
|
||||
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
|
||||
/// Storage: EthereumSystem Agents (r:1 w:0)
|
||||
/// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen)
|
||||
/// Storage: EthereumSystem Channels (r:1 w:1)
|
||||
/// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, 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: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
|
||||
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue Pages (r:1 w:1)
|
||||
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
|
||||
fn create_channel() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `602`
|
||||
// Estimated: `69050`
|
||||
// Minimum execution time: 83_000_000 picoseconds.
|
||||
Weight::from_parts(83_000_000, 69050)
|
||||
.saturating_add(RocksDbWeight::get().reads(8_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(5_u64))
|
||||
}
|
||||
/// Storage: EthereumSystem Channels (r:1 w:0)
|
||||
/// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, mode: MaxEncodedLen)
|
||||
/// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
|
||||
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue BookStateFor (r:2 w:2)
|
||||
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue ServiceHead (r:1 w:0)
|
||||
/// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue Pages (r:0 w:1)
|
||||
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
|
||||
fn update_channel() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `256`
|
||||
// Estimated: `6044`
|
||||
// Minimum execution time: 40_000_000 picoseconds.
|
||||
Weight::from_parts(40_000_000, 6044)
|
||||
.saturating_add(RocksDbWeight::get().reads(5_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
}
|
||||
/// Storage: EthereumSystem Channels (r:1 w:0)
|
||||
/// Proof: EthereumSystem Channels (max_values: None, max_size: Some(12), added: 2487, mode: MaxEncodedLen)
|
||||
/// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
|
||||
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue BookStateFor (r:2 w:2)
|
||||
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue ServiceHead (r:1 w:0)
|
||||
/// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue Pages (r:0 w:1)
|
||||
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
|
||||
fn force_update_channel() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `256`
|
||||
// Estimated: `6044`
|
||||
// Minimum execution time: 41_000_000 picoseconds.
|
||||
Weight::from_parts(41_000_000, 6044)
|
||||
.saturating_add(RocksDbWeight::get().reads(5_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
}
|
||||
/// Storage: ParachainInfo ParachainId (r:1 w:0)
|
||||
/// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
/// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
|
||||
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue ServiceHead (r:1 w:1)
|
||||
/// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue Pages (r:0 w:1)
|
||||
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
|
||||
fn set_operating_mode() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `80`
|
||||
// Estimated: `3517`
|
||||
// Minimum execution time: 31_000_000 picoseconds.
|
||||
Weight::from_parts(31_000_000, 3517)
|
||||
.saturating_add(RocksDbWeight::get().reads(4_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
}
|
||||
/// Storage: EthereumSystem Agents (r:1 w:0)
|
||||
/// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen)
|
||||
/// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
|
||||
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue BookStateFor (r:2 w:2)
|
||||
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue ServiceHead (r:1 w:0)
|
||||
/// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue Pages (r:0 w:1)
|
||||
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
|
||||
fn transfer_native_from_agent() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `252`
|
||||
// Estimated: `6044`
|
||||
// Minimum execution time: 45_000_000 picoseconds.
|
||||
Weight::from_parts(45_000_000, 6044)
|
||||
.saturating_add(RocksDbWeight::get().reads(5_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
}
|
||||
/// Storage: EthereumSystem Agents (r:1 w:0)
|
||||
/// Proof: EthereumSystem Agents (max_values: None, max_size: Some(40), added: 2515, mode: MaxEncodedLen)
|
||||
/// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
|
||||
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue BookStateFor (r:2 w:2)
|
||||
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue ServiceHead (r:1 w:0)
|
||||
/// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue Pages (r:0 w:1)
|
||||
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
|
||||
fn force_transfer_native_from_agent() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `252`
|
||||
// Estimated: `6044`
|
||||
// Minimum execution time: 42_000_000 picoseconds.
|
||||
Weight::from_parts(42_000_000, 6044)
|
||||
.saturating_add(RocksDbWeight::get().reads(5_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
}
|
||||
|
||||
/// Storage: ParachainInfo ParachainId (r:1 w:0)
|
||||
/// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
/// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
|
||||
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue ServiceHead (r:1 w:1)
|
||||
/// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue Pages (r:0 w:1)
|
||||
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
|
||||
fn set_token_transfer_fees() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `80`
|
||||
// Estimated: `3517`
|
||||
// Minimum execution time: 31_000_000 picoseconds.
|
||||
Weight::from_parts(42_000_000, 3517)
|
||||
.saturating_add(RocksDbWeight::get().reads(4_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
}
|
||||
|
||||
/// Storage: ParachainInfo ParachainId (r:1 w:0)
|
||||
/// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
|
||||
/// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
|
||||
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue ServiceHead (r:1 w:1)
|
||||
/// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen)
|
||||
/// Storage: MessageQueue Pages (r:0 w:1)
|
||||
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
|
||||
fn set_pricing_parameters() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `80`
|
||||
// Estimated: `3517`
|
||||
// Minimum execution time: 31_000_000 picoseconds.
|
||||
Weight::from_parts(42_000_000, 3517)
|
||||
.saturating_add(RocksDbWeight::get().reads(4_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user