Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Reward relayer on BH for S->E transfer #162

Closed
Closed
Show file tree
Hide file tree
Changes from 15 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
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions bridges/snowbridge/Cargo.lock

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

28 changes: 21 additions & 7 deletions bridges/snowbridge/pallets/inbound-queue/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ use snowbridge_core::{
};
use snowbridge_router_primitives::{
inbound,
inbound::{ConvertMessage, ConvertMessageError},
inbound::{Command, ConvertMessage, ConvertMessageError, MessageV1, VersionedMessage},
};
use sp_runtime::{traits::Saturating, SaturatedConversion, TokenError};

Expand All @@ -87,6 +87,7 @@ pub mod pallet {

use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use snowbridge_core::{PayMaster, PayRewardError};

#[pallet::pallet]
pub struct Pallet<T>(_);
Expand Down Expand Up @@ -141,6 +142,9 @@ pub mod pallet {

/// To withdraw and deposit an asset.
type AssetTransactor: TransactAsset;

/// To pay reward to relay
type Payer: PayMaster;
}

#[pallet::hooks]
Expand Down Expand Up @@ -188,6 +192,8 @@ pub mod pallet {
Send(SendError),
/// Message conversion error
ConvertMessage(ConvertMessageError),
/// Pay reward error
PayReward(PayRewardError),
}

#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)]
Expand Down Expand Up @@ -278,12 +284,20 @@ pub mod pallet {
}

// Decode message into XCM
let (xcm, fee) =
match inbound::VersionedMessage::decode_all(&mut envelope.payload.as_ref()) {
Ok(message) => T::MessageConverter::convert(envelope.message_id, message)
.map_err(|e| Error::<T>::ConvertMessage(e))?,
Err(_) => return Err(Error::<T>::InvalidPayload.into()),
};
let message = inbound::VersionedMessage::decode_all(&mut envelope.payload.as_ref())
.map_err(|_| Error::<T>::InvalidPayload)?;

let _ = match message {
VersionedMessage::V1(MessageV1 {
command: Command::RewardRelay { message_id, beneficiary },
..
}) => T::Payer::reward_relay(message_id, beneficiary)
.map_err(|e| Error::<T>::PayReward(e)),
_ => Ok(()),
}?;

let (xcm, fee) = T::MessageConverter::convert(envelope.message_id, message)
.map_err(|e| Error::<T>::ConvertMessage(e))?;

log::info!(
target: LOG_TARGET,
Expand Down
1 change: 1 addition & 0 deletions bridges/snowbridge/pallets/inbound-queue/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ impl inbound_queue::Config for Test {
type LengthToFee = IdentityFee<u128>;
type MaxMessageSize = ConstU32<1024>;
type AssetTransactor = SuccessfulTransactor;
type Payer = ();
}

pub fn last_events(n: usize) -> Vec<RuntimeEvent> {
Expand Down
3 changes: 3 additions & 0 deletions bridges/snowbridge/pallets/outbound-queue/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ ethabi = { package = "ethabi-decode", version = "1.0.0", default-features = fals
[dev-dependencies]
pallet-message-queue = { path = "../../../../substrate/frame/message-queue", default-features = false }
sp-keyring = { path = "../../../../substrate/primitives/keyring" }
pallet-balances = { path = "../../../../substrate/frame/balances" }

[features]
default = ["std"]
Expand Down Expand Up @@ -64,13 +65,15 @@ runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-message-queue/runtime-benchmarks",
"snowbridge-core/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-balances/try-runtime",
"pallet-message-queue/try-runtime",
"sp-runtime/try-runtime",
]
125 changes: 77 additions & 48 deletions bridges/snowbridge/pallets/outbound-queue/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,33 +107,45 @@ use bridge_hub_common::{AggregateMessageOrigin, CustomDigestItem};
use codec::Decode;
use frame_support::{
storage::StorageStreamIter,
traits::{tokens::Balance, Contains, Defensive, EnqueueMessage, Get, ProcessMessageError},
traits::{
fungible::Inspect, tokens::Balance, Contains, Defensive, EnqueueMessage, Get,
ProcessMessageError,
},
weights::{Weight, WeightToFee},
};
pub use pallet::*;
use snowbridge_core::{
outbound::{Fee, GasMeter, QueuedMessage, VersionedQueuedMessage, ETHER_DECIMALS},
outbound::{Fee, GasMeter, QueuedMessage, VersionedQueuedMessage},
BasicOperatingMode, ChannelId,
};
use snowbridge_outbound_queue_merkle_tree::merkle_root;
pub use snowbridge_outbound_queue_merkle_tree::MerkleProof;
use sp_core::{H256, U256};
use sp_core::H256;
use sp_runtime::{
traits::{CheckedDiv, Hash},
DigestItem, Saturating,
traits::{AccountIdConversion, Hash, Zero},
DigestItem,
};
use sp_std::prelude::*;
pub use types::{CommittedMessage, ProcessMessageOriginOf};
pub use weights::WeightInfo;

pub use pallet::*;
type BalanceOf<T> =
<<T as pallet::Config>::Token as Inspect<<T as frame_system::Config>::AccountId>>::Balance;

#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_support::{
pallet_prelude::*,
traits::{
fungible::{Inspect, Mutate},
tokens::Preservation,
},
PalletId,
};
use frame_system::pallet_prelude::*;
use snowbridge_core::PricingParameters;
use sp_arithmetic::FixedU128;
use snowbridge_core::{PayRewardError, PricingParameters};
use sp_arithmetic::traits::SaturatedConversion;

#[pallet::pallet]
pub struct Pallet<T>(_);
Expand Down Expand Up @@ -173,6 +185,11 @@ pub mod pallet {

/// Weight information for extrinsics in this pallet
type WeightInfo: WeightInfo;

/// Message relayers are rewarded with this asset
type Token: Mutate<Self::AccountId> + Inspect<Self::AccountId>;

type PotId: Get<PalletId>;
}

#[pallet::event]
Expand Down Expand Up @@ -243,6 +260,10 @@ pub mod pallet {
#[pallet::getter(fn operating_mode)]
pub type OperatingMode<T: Config> = StorageValue<_, BasicOperatingMode, ValueQuery>;

/// Fee locked by message hash
#[pallet::storage]
pub type LockedFee<T: Config> = StorageMap<_, Twox64Concat, H256, u128, ValueQuery>;

Comment on lines +263 to +266
Copy link
Author

@yrong yrong Jul 24, 2024

Choose a reason for hiding this comment

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

The storage map with Fee(in DOT) locked by MessageId.

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T>
where
Expand Down Expand Up @@ -280,6 +301,18 @@ pub mod pallet {
Self::deposit_event(Event::OperatingModeChanged { mode });
Ok(())
}

#[pallet::call_index(1)]
#[pallet::weight({100_000})]
pub fn add_fees(
origin: OriginFor<T>,
message_id: H256,
fee_amount: BalanceOf<T>,
) -> DispatchResult {
ensure_signed(origin)?;
Self::lock_fee(message_id, fee_amount)?;
Ok(())
}
}

impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -374,39 +407,10 @@ pub mod pallet {
/// Calculate total fee in native currency to cover all costs of delivering a message to the
/// remote destination. See module-level documentation for more details.
pub(crate) fn calculate_fee(
gas_used_at_most: u64,
params: PricingParameters<T::Balance>,
_gas_used_at_most: u64,
_params: PricingParameters<T::Balance>,
) -> Fee<T::Balance> {
// Remote fee in ether
let fee = Self::calculate_remote_fee(
gas_used_at_most,
params.fee_per_gas,
params.rewards.remote,
);

// downcast to u128
let fee: u128 = fee.try_into().defensive_unwrap_or(u128::MAX);

// multiply by multiplier and convert to local currency
let fee = FixedU128::from_inner(fee)
.saturating_mul(params.multiplier)
.checked_div(&params.exchange_rate)
.expect("exchange rate is not zero; qed")
.into_inner();

// adjust fixed point to match local currency
let fee = Self::convert_from_ether_decimals(fee);

Fee::from((Self::calculate_local_fee(), fee))
}

/// Calculate fee in remote currency for dispatching a message on Ethereum
pub(crate) fn calculate_remote_fee(
gas_used_at_most: u64,
fee_per_gas: U256,
reward: U256,
) -> U256 {
fee_per_gas.saturating_mul(gas_used_at_most.into()).saturating_add(reward)
Fee::from((Self::calculate_local_fee(), T::Balance::zero()))
Comment on lines 409 to +413
Copy link
Author

Choose a reason for hiding this comment

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

Will remove price params on chain and the remote portion of the fee.

}

/// The local component of the message processing fees in native currency
Expand All @@ -416,13 +420,38 @@ pub mod pallet {
)
}

// 1 DOT has 10 digits of precision
// 1 KSM has 12 digits of precision
// 1 ETH has 18 digits of precision
pub(crate) fn convert_from_ether_decimals(value: u128) -> T::Balance {
let decimals = ETHER_DECIMALS.saturating_sub(T::Decimals::get()) as u32;
let denom = 10u128.saturating_pow(decimals);
value.checked_div(denom).expect("divisor is non-zero; qed").into()
pub fn account_id() -> T::AccountId {
T::PotId::get().into_account_truncating()
}

pub(crate) fn lock_fee(message_id: H256, fee_amount: BalanceOf<T>) -> DispatchResult {
T::Token::mint_into(&Self::account_id(), fee_amount)?;
<LockedFee<T>>::try_mutate(message_id, |amount| -> DispatchResult {
*amount = amount.saturating_add(fee_amount.saturated_into::<u128>());
Ok(())
})?;
Ok(())
}

pub(crate) fn unlock_fee(
message_id: H256,
beneficiary: [u8; 32],
) -> Result<(), PayRewardError>
where
<T as frame_system::Config>::AccountId: From<[u8; 32]>,
{
let amount: BalanceOf<T> = <LockedFee<T>>::get(message_id).saturated_into();

T::Token::transfer(
&Self::account_id(),
&beneficiary.into(),
amount,
Preservation::Preserve,
)
.map_err(|_| PayRewardError::UnlockFailed)?;

<LockedFee<T>>::remove(message_id);
Ok(())
}
}
}
34 changes: 31 additions & 3 deletions bridges/snowbridge/pallets/outbound-queue/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use frame_support::{
derive_impl, parameter_types,
traits::{Everything, Hooks},
weights::IdentityFee,
PalletId,
};

use snowbridge_core::{
Expand All @@ -16,23 +17,26 @@ use snowbridge_core::{
};
use sp_core::{ConstU32, ConstU8, H160, H256};
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup, Keccak256},
AccountId32, BuildStorage, FixedU128,
traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Keccak256, Verify},
BuildStorage, FixedU128, MultiSignature,
};
use sp_std::marker::PhantomData;

type Block = frame_system::mocking::MockBlock<Test>;
type AccountId = AccountId32;

frame_support::construct_runtime!(
pub enum Test
{
System: frame_system::{Pallet, Call, Storage, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event<T>},
OutboundQueue: crate::{Pallet, Storage, Event<T>},
}
);

pub type Signature = MultiSignature;
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;

#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
type BaseCallFilter = Everything;
Expand All @@ -45,10 +49,30 @@ impl frame_system::Config for Test {
type Lookup = IdentityLookup<Self::AccountId>;
type RuntimeEvent = RuntimeEvent;
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u128>;
type Nonce = u64;
type Block = Block;
}

parameter_types! {
pub const ExistentialDeposit: u128 = 1;
}
impl pallet_balances::Config for Test {
type MaxLocks = ();
type MaxReserves = ();
type ReserveIdentifier = [u8; 8];
type Balance = u128;
type RuntimeEvent = RuntimeEvent;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type WeightInfo = ();
type FreezeIdentifier = ();
type MaxFreezes = ();
type RuntimeHoldReason = ();
type RuntimeFreezeReason = ();
}

parameter_types! {
pub const HeapSize: u32 = 32 * 1024;
pub const MaxStale: u32 = 32;
Expand Down Expand Up @@ -76,6 +100,7 @@ parameter_types! {
rewards: Rewards { local: DOT, remote: meth(1) },
multiplier: FixedU128::from_rational(4, 3),
};
pub const PotId: PalletId = PalletId(*b"SwOutQue");
}

pub const DOT: u128 = 10_000_000_000;
Expand All @@ -93,6 +118,8 @@ impl crate::Config for Test {
type Channels = Everything;
type WeightToFee = IdentityFee<u128>;
type WeightInfo = ();
type Token = Balances;
type PotId = PotId;
}

fn setup() {
Expand Down Expand Up @@ -170,6 +197,7 @@ pub fn mock_message(sibling_para_id: u32) -> Message {
token: Default::default(),
recipient: Default::default(),
amount: 0,
fee_amount: 1,
},
},
}
Expand Down
Loading
Loading