Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0b35bc6
Register PNA with fee
yrong Jun 3, 2025
f719fbe
Burn fee for adding a tip
yrong Jun 3, 2025
09e61be
Fix runtime
yrong Jun 3, 2025
d2f43b9
Refactor function swap_fee_asset_and_burn
yrong Jun 3, 2025
8c85471
Refactor a common function send_transact_call
yrong Jun 3, 2025
b759f57
Fix the breaking tests
yrong Jun 3, 2025
5d433e9
Add prdoc
yrong Jun 3, 2025
d82423d
Merge branch 'master' into register-pna-with-fee
yrong Jun 3, 2025
d87e93e
Fix benchmark
yrong Jun 4, 2025
38ed577
Merge branch 'register-pna-with-fee' of https://github.com/yrong/polk…
yrong Jun 4, 2025
629386f
Fix semver
yrong Jun 4, 2025
8f55705
Use Location as parameter
yrong Jun 4, 2025
029da09
Allow zero fee
yrong Jun 4, 2025
bf2fabe
Merge branch 'master' into register-pna-with-fee
yrong Jun 4, 2025
27063ec
Remove the DescribeTerminus converter
yrong Jun 4, 2025
ac05cc3
Merge branch 'master' into register-pna-with-fee
yrong Jun 4, 2025
7f764c6
Benchmark with swapped Ether
yrong Jun 4, 2025
e56fe4c
Merge branch 'register-pna-with-fee' of https://github.com/yrong/polk…
yrong Jun 4, 2025
569c342
More tests
yrong Jun 4, 2025
5df1115
Skip fee handling for Root location
yrong Jun 4, 2025
e2e004e
Add backend execution fee
yrong Jun 5, 2025
6a1fae5
Merge branch 'master' into register-pna-with-fee
yrong Jun 5, 2025
5a3e966
Check root origin first
yrong Jun 5, 2025
c63ab8b
Merge branch 'register-pna-with-fee' of https://github.com/yrong/polk…
yrong Jun 5, 2025
55c4f61
Merge branch 'master' into register-pna-with-fee
yrong Jun 5, 2025
946f280
Merge branch 'master' into register-pna-with-fee
yrong Jun 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
//! XCM Execution weights for invoking the backend implementation

use frame_support::weights::Weight;
use frame_support::weights::{constants::RocksDbWeight, Weight};

/// XCM Execution weights for invoking the backend implementation
pub trait BackendWeightInfo {
/// Execution weight for remote xcm that dispatches `EthereumSystemCall::RegisterToken`
/// using `Transact`.
fn transact_register_token() -> Weight;
fn transact_add_tip() -> Weight;
fn do_process_message() -> Weight;
fn commit_single() -> Weight;
fn submit_delivery_receipt() -> Weight;
}

impl BackendWeightInfo for () {
Expand All @@ -19,4 +22,21 @@ impl BackendWeightInfo for () {
fn transact_add_tip() -> Weight {
Weight::from_parts(100_000_000, 10000)
}
fn do_process_message() -> Weight {
Weight::from_parts(39_000_000, 3485)
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(4_u64))
}
fn commit_single() -> Weight {
Weight::from_parts(9_000_000, 1586)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}

fn submit_delivery_receipt() -> Weight {
Weight::from_parts(70_000_000, 0)
.saturating_add(Weight::from_parts(0, 3601))
.saturating_add(RocksDbWeight::get().reads(2))
.saturating_add(RocksDbWeight::get().writes(2))
}
}
11 changes: 9 additions & 2 deletions bridges/snowbridge/pallets/system-frontend/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::Pallet as SnowbridgeControlFrontend;
use frame_benchmarking::v2::*;
use frame_system::RawOrigin;
use xcm::prelude::{Location, *};
use xcm_executor::traits::ConvertLocation;

#[benchmarks(where <T as frame_system::Config>::AccountId: Into<Location>)]
mod benchmarks {
Expand All @@ -18,16 +19,22 @@ mod benchmarks {

let asset_location: Location = Location::new(1, [Parachain(2000), GeneralIndex(1)]);
let asset_id = Box::new(VersionedLocation::from(asset_location.clone()));
T::Helper::initialize_storage(asset_location, origin_location);
T::Helper::initialize_storage(asset_location, origin_location.clone());

let ether = T::EthereumLocation::get();
let asset_owner = T::AccountIdConverter::convert_location(&origin_location).unwrap();
T::Helper::setup_pools(asset_owner, ether.clone());

let asset_metadata = AssetMetadata {
name: "pal".as_bytes().to_vec().try_into().unwrap(),
symbol: "pal".as_bytes().to_vec().try_into().unwrap(),
decimals: 12,
};

let fee_asset = Asset::from((Location::parent(), 1_000_000u128));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe 1_000_000u128 should be a const with some descriptive name?


#[extrinsic_call]
_(origin as T::RuntimeOrigin, asset_id, asset_metadata);
_(origin as T::RuntimeOrigin, asset_id, asset_metadata, fee_asset);

Ok(())
}
Expand Down
142 changes: 83 additions & 59 deletions bridges/snowbridge/pallets/system-frontend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub enum EthereumSystemCall<T: frame_system::Config> {
sender: Box<VersionedLocation>,
asset_id: Box<VersionedLocation>,
metadata: AssetMetadata,
amount: u128,
},
#[codec(index = 3)]
AddTip { sender: AccountIdOf<T>, message_id: MessageId, amount: u128 },
Expand All @@ -80,6 +81,7 @@ where
#[frame_support::pallet]
pub mod pallet {
use super::*;
use xcm_executor::traits::ConvertLocation;
#[pallet::pallet]
pub struct Pallet<T>(_);

Expand Down Expand Up @@ -118,6 +120,8 @@ pub mod pallet {
/// InteriorLocation of this pallet.
type PalletLocation: Get<InteriorLocation>;

type AccountIdConverter: ConvertLocation<Self::AccountId>;
Comment thread
acatangiu marked this conversation as resolved.

/// Weights for dispatching XCM to backend implementation of `register_token`
type BackendWeightInfo: BackendWeightInfo;

Expand Down Expand Up @@ -215,33 +219,37 @@ pub mod pallet {
#[pallet::weight(
T::WeightInfo::register_token()
.saturating_add(T::BackendWeightInfo::transact_register_token())
.saturating_add(T::BackendWeightInfo::do_process_message())
.saturating_add(T::BackendWeightInfo::commit_single())
.saturating_add(T::BackendWeightInfo::submit_delivery_receipt())
)]
pub fn register_token(
origin: OriginFor<T>,
asset_id: Box<VersionedLocation>,
metadata: AssetMetadata,
fee_asset: Asset,
) -> DispatchResult {
ensure!(!Self::export_operating_mode().is_halted(), Error::<T>::Halted);

let asset_location: Location =
(*asset_id).try_into().map_err(|_| Error::<T>::UnsupportedLocationVersion)?;
let origin_location = T::RegisterTokenOrigin::ensure_origin(origin, &asset_location)?;

let dest = T::BridgeHubLocation::get();
let call =
Self::build_register_token_call(origin_location.clone(), asset_location, metadata)?;
let remote_xcm = Self::build_remote_xcm(&call);
let message_id = Self::send_xcm(origin_location, dest.clone(), remote_xcm.clone())
.map_err(|error| Error::<T>::from(error))?;
let ether_gained = if origin_location.is_here() {
// Root origin/location does not pay any fees/tip.
0
} else {
Self::swap_fee_asset_and_burn(origin_location.clone(), fee_asset)?
};

Self::deposit_event(Event::<T>::MessageSent {
origin: T::PalletLocation::get().into(),
destination: dest,
message: remote_xcm,
message_id,
});
let call = Self::build_register_token_call(
origin_location.clone(),
asset_location,
metadata,
ether_gained,
)?;

Ok(())
Self::send_transact_call(origin_location, call)
}

/// Add an additional relayer tip for a committed message identified by `message_id`.
Expand All @@ -257,49 +265,12 @@ pub mod pallet {
{
let who = ensure_signed(origin)?;

let ether_location = T::EthereumLocation::get();
let (tip_asset_location, tip_amount) = match asset {
Asset { id: AssetId(ref loc), fun: Fungibility::Fungible(amount) } => (loc, amount),
_ => {
tracing::debug!(target: LOG_TARGET, ?asset, "error matching tip asset");
return Err(Error::<T>::UnsupportedAsset.into())
},
};

ensure!(tip_amount > 0, Error::<T>::TipAmountZero);

let ether_gained = if *tip_asset_location != ether_location {
Self::swap_and_burn(
who.clone(),
tip_asset_location.clone(),
ether_location,
tip_amount,
)
.inspect_err(|&e| {
tracing::debug!(target: LOG_TARGET, ?e, "error swapping asset");
})?
} else {
tip_amount
};
let ether_gained = Self::swap_fee_asset_and_burn(who.clone().into(), asset)?;

// Send the tip details to BH to be allocated to the reward in the Inbound/Outbound
// pallet
let dest = T::BridgeHubLocation::get();
let call = Self::build_add_tip_call(who.clone(), message_id.clone(), ether_gained);
let remote_xcm = Self::build_remote_xcm(&call);
let who_location: Location = who.into();

let xcm_message_id = Self::send_xcm(who_location, dest.clone(), remote_xcm.clone())
.map_err(|error| Error::<T>::from(error))?;

Self::deposit_event(Event::<T>::MessageSent {
origin: T::PalletLocation::get().into(),
destination: dest,
message: remote_xcm,
message_id: xcm_message_id,
});

Ok(())
Self::send_transact_call(who.into(), call)
}
}

Expand All @@ -318,17 +289,15 @@ pub mod pallet {
/// teleportation. Returns the amount of Ether gained if successful, or a DispatchError if
/// any step fails.
fn swap_and_burn(
who: AccountIdOf<T>,
origin: Location,
tip_asset_location: Location,
ether_location: Location,
tip_amount: u128,
) -> Result<u128, DispatchError>
where
<T as frame_system::Config>::AccountId: Into<Location>,
{
) -> Result<u128, DispatchError> {
// Swap tip asset to ether
let swap_path = vec![tip_asset_location.clone(), ether_location.clone()];
let who_location: Location = who.clone().into();
let who = T::AccountIdConverter::convert_location(&origin)
.ok_or(Error::<T>::LocationConversionFailed)?;

let ether_gained = T::Swap::swap_exact_tokens_for_tokens(
who.clone(),
Expand All @@ -342,7 +311,7 @@ pub mod pallet {
// Burn the ether
let ether_asset = Asset::from((ether_location.clone(), ether_gained));

burn_for_teleport::<T::AssetTransactor>(&who_location, &ether_asset)
burn_for_teleport::<T::AssetTransactor>(&origin, &ether_asset)
.map_err(|_| Error::<T>::BurnError)?;

Ok(ether_gained)
Expand All @@ -353,6 +322,7 @@ pub mod pallet {
sender: Location,
asset: Location,
metadata: AssetMetadata,
amount: u128,
) -> Result<BridgeHubRuntime<T>, Error<T>> {
// reanchor locations relative to BH
let sender = Self::reanchored(sender)?;
Expand All @@ -362,6 +332,7 @@ pub mod pallet {
sender: Box::new(VersionedLocation::from(sender)),
asset_id: Box::new(VersionedLocation::from(asset)),
metadata,
amount,
});

Ok(call)
Expand Down Expand Up @@ -398,6 +369,59 @@ pub mod pallet {
.reanchored(&T::BridgeHubLocation::get(), &T::UniversalLocation::get())
.map_err(|_| Error::<T>::LocationConversionFailed)
}

fn swap_fee_asset_and_burn(
origin: Location,
fee_asset: Asset,
) -> Result<u128, DispatchError> {
let ether_location = T::EthereumLocation::get();
let (fee_asset_location, fee_amount) = match fee_asset {
Asset { id: AssetId(ref loc), fun: Fungible(amount) } => (loc, amount),
_ => {
tracing::debug!(target: LOG_TARGET, ?fee_asset, "error matching fee asset");
return Err(Error::<T>::UnsupportedAsset.into())
},
};
if fee_amount == 0 {
return Ok(0)
}

let ether_gained = if *fee_asset_location != ether_location {
Self::swap_and_burn(
origin.clone(),
fee_asset_location.clone(),
ether_location,
fee_amount,
)
.inspect_err(|&e| {
tracing::debug!(target: LOG_TARGET, ?e, "error swapping asset");
})?
} else {
burn_for_teleport::<T::AssetTransactor>(&origin, &fee_asset)
.map_err(|_| Error::<T>::BurnError)?;
fee_amount
};
Ok(ether_gained)
}

fn send_transact_call(
origin_location: Location,
call: BridgeHubRuntime<T>,
) -> DispatchResult {
let dest = T::BridgeHubLocation::get();
let remote_xcm = Self::build_remote_xcm(&call);
let message_id = Self::send_xcm(origin_location, dest.clone(), remote_xcm.clone())
.map_err(|error| Error::<T>::from(error))?;

Self::deposit_event(Event::<T>::MessageSent {
origin: T::PalletLocation::get().into(),
destination: dest,
message: remote_xcm,
message_id,
});

Ok(())
}
}

impl<T: Config> ExportPausedQuery for Pallet<T> {
Expand Down
16 changes: 15 additions & 1 deletion bridges/snowbridge/pallets/system-frontend/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ use frame_support::{
derive_impl, parameter_types,
traits::{AsEnsureOriginWithArg, Everything},
};
use snowbridge_core::ParaId;
use snowbridge_test_utils::mock_swap_executor::SwapExecutor;
pub use snowbridge_test_utils::{mock_origin::pallet_xcm_origin, mock_xcm::*};
use sp_core::H256;
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
traits::{AccountIdConversion, BlakeTwo256, IdentityLookup},
AccountId32, BuildStorage,
};
use xcm::prelude::*;
Expand Down Expand Up @@ -75,6 +76,18 @@ parameter_types! {
pub PalletLocation: InteriorLocation = [PalletInstance(36)].into();
}

pub struct AccountIdConverter;
impl xcm_executor::traits::ConvertLocation<AccountId> for AccountIdConverter {
fn convert_location(ml: &Location) -> Option<AccountId> {
match ml.unpack() {
(0, [Junction::AccountId32 { id, .. }]) =>
Some(<AccountId as codec::Decode>::decode(&mut &*id.to_vec()).unwrap()),
(1, [Parachain(id)]) => Some(ParaId::from(*id).into_account_truncating()),
_ => None,
}
}
}

impl crate::Config for Test {
type RuntimeEvent = RuntimeEvent;
type RegisterTokenOrigin = AsEnsureOriginWithArg<pallet_xcm_origin::EnsureXcm<Everything>>;
Expand All @@ -90,6 +103,7 @@ impl crate::Config for Test {
type WeightInfo = ();
#[cfg(feature = "runtime-benchmarks")]
type Helper = ();
type AccountIdConverter = AccountIdConverter;
}

// Build genesis storage according to the mock runtime.
Expand Down
Loading
Loading