Skip to content

Commit

Permalink
fixes and cleaner implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
alistair-singh committed Nov 9, 2023
1 parent 7741b64 commit f360d7b
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 80 deletions.
2 changes: 0 additions & 2 deletions parachain/primitives/runtime-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ log = { version = "0.4.20", default-features = false }

bp-rococo = { path = "../../../polkadot-sdk/bridges/primitives/chain-rococo", default-features = false }
frame-support = { path = "../../../polkadot-sdk/substrate/frame/support", default-features = false }
sp-io = { path = "../../../polkadot-sdk/substrate/primitives/io", default-features = false }
xcm = { package = "staging-xcm", path = "../../../polkadot-sdk/polkadot/xcm", default-features = false }
xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot-sdk/polkadot/xcm/xcm-builder", default-features = false }
xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot-sdk/polkadot/xcm/xcm-executor", default-features = false }
Expand All @@ -24,7 +23,6 @@ std = [
"bp-rococo/std",
"frame-support/std",
"snowbridge-core/std",
"sp-io/std",
"xcm-builder/std",
"xcm-executor/std",
"xcm/std",
Expand Down
148 changes: 71 additions & 77 deletions parachain/primitives/runtime-common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
//! # Runtime Common
//!
//! Common traits and types shared by runtimes.
#![cfg_attr(not(feature = "std"), no_std)]

use bp_rococo::AccountId;
use core::marker::PhantomData;
use frame_support::traits::Get;
Expand All @@ -7,105 +14,92 @@ use xcm_builder::{deposit_or_burn_fee, HandleFee};
use xcm_executor::traits::{FeeReason, TransactAsset};

/// A `HandleFee` implementation that takes fees from `ExportMessage` XCM instructions
/// to Snowbridge and holds it in a receiver account. Burns the fees in case of a failure.
pub struct XcmExportFeeToSnowbridge<
TokenLocation,
EthereumNetwork,
ReceiverAccount,
AssetTransactor,
OutboundQueue,
>(PhantomData<(TokenLocation, EthereumNetwork, ReceiverAccount, AssetTransactor, OutboundQueue)>);
/// to Snowbridge and splits it between the origin parachains sovereign and the system
/// treasury account specified by `ReceiverAmount`.
pub struct XcmExportFeeToSnowbridge<TokenLocation, EthereumNetwork, AssetTransactor, OutboundQueue>(
PhantomData<(TokenLocation, EthereumNetwork, AssetTransactor, OutboundQueue)>,
);

impl<
TokenLocation: Get<MultiLocation>,
EthereumNetwork: Get<NetworkId>,
ReceiverAccount: Get<AccountId>,
AssetTransactor: TransactAsset,
OutboundQueue: SendMessageFeeProvider<Balance = bp_rococo::Balance>,
> HandleFee
for XcmExportFeeToSnowbridge<
TokenLocation,
EthereumNetwork,
ReceiverAccount,
AssetTransactor,
OutboundQueue,
>
for XcmExportFeeToSnowbridge<TokenLocation, EthereumNetwork, AssetTransactor, OutboundQueue>
{
fn handle_fee(
fees: MultiAssets,
context: Option<&XcmContext>,
reason: FeeReason,
) -> MultiAssets {
let token_location = TokenLocation::get();
let mut fees = fees.into_inner();

if matches!(reason, FeeReason::Export { network: bridged_network, destination }
if bridged_network == EthereumNetwork::get() && destination == Here)
{
log::info!(
target: "xcm::fees",
"XcmExportFeeToSnowbridge fees: {fees:?}, context: {context:?}, reason: {reason:?}",
);
// Check the reason to see if this export is for snowbridge.
let snowbridge_export = matches!(
reason,
FeeReason::Export { network: bridged_network, destination }
if bridged_network == EthereumNetwork::get() && destination == Here
);

let fee_item_index = fees.iter().position(|asset| {
matches!(
asset,
MultiAsset { id: Concrete(location), fun: Fungible(..)}
if *location == token_location,
)
});
// Find the fee asset.
let fee_item = if let Some(element) = fee_item_index {
fees.remove(element)
// Get the parachain sovereign from the `context`.
let maybe_para_sovereign = if let Some(XcmContext {
origin: Some(MultiLocation { parents: 1, interior }),
..
}) = context
{
if let Some(Parachain(sibling_para_id)) = interior.first() {
let account: AccountId =
sibling_sovereign_account_raw((*sibling_para_id).into()).into();
Some(account)
} else {
return fees.into()
};

let receiver = ReceiverAccount::get();
// There is an origin so split fee into parts.
if let Some(XcmContext {
origin: Some(MultiLocation { parents: 1, interior }), ..
}) = context
{
if let Some(Parachain(sibling_para_id)) = interior.first() {
let account: AccountId =
sibling_sovereign_account_raw((*sibling_para_id).into()).into();
let local_fee = OutboundQueue::local_fee();
if let Fungible(amount) = fee_item.fun {
let remote_fee = amount.saturating_sub(local_fee);
None
}
} else {
None
};

// Send local fee to receiver
deposit_or_burn_fee::<AssetTransactor, _>(
MultiAsset {
id: Concrete(token_location),
fun: Fungible(amount - remote_fee),
}
.into(),
context,
receiver,
);
// Send remote fee to origin
deposit_or_burn_fee::<AssetTransactor, _>(
MultiAsset { id: Concrete(token_location), fun: Fungible(remote_fee) }
.into(),
context,
account,
);
} else {
// Push the fee item back and bail out to let other handlers run.
fees.push(fee_item);
return fees.into()
// Get the total fee offered by export message.
let maybe_total_supplied_fee = fees
.inner()
.iter()
.enumerate()
.filter_map(|(index, asset)| {
if let MultiAsset { id: Concrete(location), fun: Fungible(amount) } = asset {
if *location == token_location {
return Some((index, amount))
}
} else {
// Origin conversion failed so send the full fee to the receiver.
deposit_or_burn_fee::<AssetTransactor, _>(fee_item.into(), context, receiver);
}
} else {
// There is no context so send the full fee to the receiver.
deposit_or_burn_fee::<AssetTransactor, _>(fee_item.into(), context, receiver);
return None
})
.next();

if let (true, Some(para_sovereign), Some((fee_index, total_fee))) =
(snowbridge_export, maybe_para_sovereign, maybe_total_supplied_fee)
{
let remote_fee = total_fee.saturating_sub(OutboundQueue::local_fee());
if remote_fee > 0 {
// Send remote fee to origin
deposit_or_burn_fee::<AssetTransactor, _>(
MultiAsset { id: Concrete(token_location), fun: Fungible(remote_fee) }.into(),
context,
para_sovereign,
);
// Return remaining fee to the next fee handler in the chain.
let mut modified_fees = fees.inner().clone();
modified_fees.remove(fee_index);
modified_fees.push(MultiAsset {
id: Concrete(token_location),
fun: Fungible(total_fee - remote_fee),
});
return modified_fees.into()
}
}

fees.into()
log::trace!(
target: "xcm::fees",
"XcmExportFeeToSnowbridge skipped: {fees:?}, context: {context:?}, reason: {reason:?}",
);
return fees
}
}
2 changes: 1 addition & 1 deletion polkadot-sdk

0 comments on commit f360d7b

Please sign in to comment.