Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 13 additions & 3 deletions bridges/snowbridge/runtime/runtime-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,27 @@ exclude-from-umbrella = true
[dependencies]
codec = { workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
log = { workspace = true }
pallet-xcm = { workspace = true }
snowbridge-core = { workspace = true }
snowbridge-outbound-queue-primitives = { workspace = true }
sp-arithmetic = { workspace = true }
sp-std = { workspace = true }
xcm = { workspace = true }
xcm-builder = { workspace = true }
xcm-executor = { workspace = true }

snowbridge-core = { workspace = true }
snowbridge-outbound-queue-primitives = { workspace = true }

[dev-dependencies]

[features]
default = ["std"]
std = [
"codec/std",
"frame-support/std",
"frame-system/std",
"log/std",
"pallet-xcm/std",
"snowbridge-core/std",
"snowbridge-outbound-queue-primitives/std",
"sp-arithmetic/std",
Expand All @@ -45,8 +48,15 @@ std = [
]
runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-xcm/runtime-benchmarks",
"snowbridge-core/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
"xcm/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-xcm/try-runtime",
]
148 changes: 4 additions & 144 deletions bridges/snowbridge/runtime/runtime-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,149 +4,9 @@
//!
//! Common traits and types shared by runtimes.
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(test)]
mod tests;

use codec::FullCodec;
use core::marker::PhantomData;
use frame_support::traits::Get;
use snowbridge_outbound_queue_primitives::SendMessageFeeProvider;
use sp_arithmetic::traits::{BaseArithmetic, Unsigned};
use sp_std::fmt::Debug;
use xcm::prelude::*;
use xcm_builder::HandleFee;
use xcm_executor::traits::{FeeReason, TransactAsset};

pub const LOG_TARGET: &str = "xcm::export-fee-to-sibling";

/// A `HandleFee` implementation that takes fees from `ExportMessage` XCM instructions
/// to Snowbridge and splits off the remote fee and deposits it to the origin
/// parachain sovereign account. The local fee is then returned back to be handled by
/// the next fee handler in the chain. Most likely the treasury account.
pub struct XcmExportFeeToSibling<
Balance,
AccountId,
FeeAssetLocation,
EthereumNetwork,
AssetTransactor,
FeeProvider,
>(
PhantomData<(
Balance,
AccountId,
FeeAssetLocation,
EthereumNetwork,
AssetTransactor,
FeeProvider,
)>,
);

impl<Balance, AccountId, FeeAssetLocation, EthereumNetwork, AssetTransactor, FeeProvider> HandleFee
for XcmExportFeeToSibling<
Balance,
AccountId,
FeeAssetLocation,
EthereumNetwork,
AssetTransactor,
FeeProvider,
>
where
Balance: BaseArithmetic + Unsigned + Copy + From<u128> + Into<u128> + Debug,
AccountId: Clone + FullCodec,
FeeAssetLocation: Get<Location>,
EthereumNetwork: Get<NetworkId>,
AssetTransactor: TransactAsset,
FeeProvider: SendMessageFeeProvider<Balance = Balance>,
{
fn handle_fee(fees: Assets, context: Option<&XcmContext>, reason: FeeReason) -> Assets {
let token_location = FeeAssetLocation::get();

// Check the reason to see if this export is for snowbridge.
if !matches!(
reason,
FeeReason::Export { network: bridged_network, ref destination }
if bridged_network == EthereumNetwork::get() && destination == &Here
) {
return fees
}

// Get the parachain sovereign from the `context`.
let maybe_para_id: Option<u32> =
if let Some(XcmContext { origin: Some(Location { parents: 1, interior }), .. }) =
context
{
if let Some(Parachain(sibling_para_id)) = interior.first() {
Some(*sibling_para_id)
} else {
None
}
} else {
None
};
if maybe_para_id.is_none() {
log::error!(
target: LOG_TARGET,
"invalid location in context {:?}",
context,
);
return fees
}
let para_id = maybe_para_id.unwrap();

// Get the total fee offered by export message.
let maybe_total_supplied_fee: Option<(usize, Balance)> = fees
.inner()
.iter()
.enumerate()
.filter_map(|(index, asset)| {
if let Asset { id: location, fun: Fungible(amount) } = asset {
if location.0 == token_location {
return Some((index, (*amount).into()))
}
}
None
})
.next();
if maybe_total_supplied_fee.is_none() {
log::error!(
target: LOG_TARGET,
"could not find fee asset item in fees: {:?}",
fees,
);
return fees
}
let (fee_index, total_fee) = maybe_total_supplied_fee.unwrap();
let local_fee = FeeProvider::local_fee();
let remote_fee = total_fee.saturating_sub(local_fee);
if local_fee == Balance::zero() || remote_fee == Balance::zero() {
log::error!(
target: LOG_TARGET,
"calculated refund incorrect with local_fee: {:?} and remote_fee: {:?}",
local_fee,
remote_fee,
);
return fees
}
// Refund remote component of fee to physical origin
let result = AssetTransactor::deposit_asset(
&Asset { id: AssetId(token_location.clone()), fun: Fungible(remote_fee.into()) },
&Location::new(1, [Parachain(para_id)]),
context,
);
if result.is_err() {
log::error!(
target: LOG_TARGET,
"transact fee asset failed: {:?}",
result.unwrap_err()
);
return fees
}

// 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(Asset { id: AssetId(token_location), fun: Fungible(local_fee.into()) });
modified_fees.into()
}
}
pub mod v1;
pub mod v2;
pub use v1::fee_handler::XcmExportFeeToSibling;
pub use v2::register_token::{ForeignAssetOwner, LocalAssetOwner};
146 changes: 146 additions & 0 deletions bridges/snowbridge/runtime/runtime-common/src/v1/fee_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
//! Handling of fees for `ExportMessage` on BridgeHub in V1.

use codec::FullCodec;
use core::marker::PhantomData;
use frame_support::traits::Get;
use snowbridge_outbound_queue_primitives::SendMessageFeeProvider;
use sp_arithmetic::traits::{BaseArithmetic, Unsigned};
use sp_std::fmt::Debug;
use xcm::prelude::*;
use xcm_builder::HandleFee;
use xcm_executor::traits::{FeeReason, TransactAsset};

pub const LOG_TARGET: &str = "xcm::export-fee-to-sibling";

/// A `HandleFee` implementation that takes fees from `ExportMessage` XCM instructions
/// to Snowbridge and splits off the remote fee and deposits it to the origin
/// parachain sovereign account. The local fee is then returned back to be handled by
/// the next fee handler in the chain. Most likely the treasury account.
pub struct XcmExportFeeToSibling<
Balance,
AccountId,
FeeAssetLocation,
EthereumNetwork,
AssetTransactor,
FeeProvider,
>(
PhantomData<(
Balance,
AccountId,
FeeAssetLocation,
EthereumNetwork,
AssetTransactor,
FeeProvider,
)>,
);

impl<Balance, AccountId, FeeAssetLocation, EthereumNetwork, AssetTransactor, FeeProvider> HandleFee
for XcmExportFeeToSibling<
Balance,
AccountId,
FeeAssetLocation,
EthereumNetwork,
AssetTransactor,
FeeProvider,
>
where
Balance: BaseArithmetic + Unsigned + Copy + From<u128> + Into<u128> + Debug,
AccountId: Clone + FullCodec,
FeeAssetLocation: Get<Location>,
EthereumNetwork: Get<NetworkId>,
AssetTransactor: TransactAsset,
FeeProvider: SendMessageFeeProvider<Balance = Balance>,
{
fn handle_fee(fees: Assets, context: Option<&XcmContext>, reason: FeeReason) -> Assets {
let token_location = FeeAssetLocation::get();

// Check the reason to see if this export is for snowbridge.
if !matches!(
reason,
FeeReason::Export { network: bridged_network, ref destination }
if bridged_network == EthereumNetwork::get() && destination == &Here
) {
return fees
}

// Get the parachain sovereign from the `context`.
let maybe_para_id: Option<u32> =
if let Some(XcmContext { origin: Some(Location { parents: 1, interior }), .. }) =
context
{
if let Some(Parachain(sibling_para_id)) = interior.first() {
Some(*sibling_para_id)
} else {
None
}
} else {
None
};
if maybe_para_id.is_none() {
log::error!(
target: LOG_TARGET,
"invalid location in context {:?}",
context,
);
return fees
}
let para_id = maybe_para_id.unwrap();

// Get the total fee offered by export message.
let maybe_total_supplied_fee: Option<(usize, Balance)> = fees
.inner()
.iter()
.enumerate()
.filter_map(|(index, asset)| {
if let Asset { id: location, fun: Fungible(amount) } = asset {
if location.0 == token_location {
return Some((index, (*amount).into()))
}
}
None
})
.next();
if maybe_total_supplied_fee.is_none() {
log::error!(
target: LOG_TARGET,
"could not find fee asset item in fees: {:?}",
fees,
);
return fees
}
let (fee_index, total_fee) = maybe_total_supplied_fee.unwrap();
let local_fee = FeeProvider::local_fee();
let remote_fee = total_fee.saturating_sub(local_fee);
if local_fee == Balance::zero() || remote_fee == Balance::zero() {
log::error!(
target: LOG_TARGET,
"calculated refund incorrect with local_fee: {:?} and remote_fee: {:?}",
local_fee,
remote_fee,
);
return fees
}
// Refund remote component of fee to physical origin
let result = AssetTransactor::deposit_asset(
&Asset { id: AssetId(token_location.clone()), fun: Fungible(remote_fee.into()) },
&Location::new(1, [Parachain(para_id)]),
context,
);
if result.is_err() {
log::error!(
target: LOG_TARGET,
"transact fee asset failed: {:?}",
result.unwrap_err()
);
return fees
}

// 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(Asset { id: AssetId(token_location), fun: Fungible(local_fee.into()) });
modified_fees.into()
}
}
3 changes: 3 additions & 0 deletions bridges/snowbridge/runtime/runtime-common/src/v1/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
pub mod fee_handler;
3 changes: 3 additions & 0 deletions bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
pub mod register_token;
Loading