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
10 changes: 4 additions & 6 deletions bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,10 @@ pub mod pallet {
})?;

// Pay relayer reward
if !relayer_fee.is_zero() {
T::RewardPayment::register_reward(
&relayer,
T::DefaultRewardKind::get(),
relayer_fee,
);
let tip = Tips::<T>::take(nonce).unwrap_or_default();
let total_tip = relayer_fee.saturating_add(tip);
if total_tip > 0 {
T::RewardPayment::register_reward(&relayer, T::DefaultRewardKind::get(), total_tip);
}

Self::deposit_event(Event::MessageReceived { nonce, message_id });
Expand Down
140 changes: 139 additions & 1 deletion bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use codec::Encode;
use frame_support::{assert_noop, assert_ok};
use snowbridge_inbound_queue_primitives::{v2::XcmPayload, EventProof, Proof};
use snowbridge_test_utils::{
mock_rewards::RegisteredRewardsCount,
mock_rewards::{RegisteredRewardAmount, RegisteredRewardsCount},
mock_xcm::{set_charge_fees_override, set_sender_override},
};
use sp_keyring::sr25519::Keyring;
Expand Down Expand Up @@ -389,3 +389,141 @@ fn test_add_tip_amount_zero() {
assert_eq!(Tips::<Test>::get(nonce), None);
});
}

#[test]
fn inbound_tip_is_paid_out_to_relayer() {
new_tester().execute_with(|| {
let nonce: u64 = 77;
let tip: u128 = 12_345;
let relayer_fee: u128 = 2_000;

// Add tip for nonce before message is processed
assert_ok!(InboundQueue::add_tip(nonce, tip));
assert_eq!(Tips::<Test>::get(nonce), Some(tip));

// Process inbound message with relayer_fee
let relayer: AccountId = Keyring::Bob.into();
assert_ok!(InboundQueue::process_message(
relayer,
Message {
nonce,
assets: vec![],
xcm: XcmPayload::Raw(vec![]),
claimer: None,
execution_fee: 1_000_000_000,
relayer_fee,
gateway: mock::GatewayAddress::get(),
origin: H160::random(),
value: 3_000_000_000,
},
));

// Reward should be registered from relayer_fee + tip
assert_eq!(
RegisteredRewardsCount::get(),
1,
"Reward should be registered from relayer_fee + tip"
);

// Check the actual reward amount paid out (should be relayer_fee + tip)
assert_eq!(
RegisteredRewardAmount::get(),
relayer_fee + tip,
"Reward amount should equal relayer_fee + tip"
);

// Tip should be consumed from storage
assert_eq!(Tips::<Test>::get(nonce), None);
});
}

#[test]
fn relayer_fee_paid_out_when_no_tip_exists() {
new_tester().execute_with(|| {
let nonce: u64 = 88;
let relayer_fee: u128 = 5_000;

// Ensure no tip exists for this nonce
assert_eq!(Tips::<Test>::get(nonce), None);

// Process inbound message with relayer_fee but no tip
let relayer: AccountId = Keyring::Bob.into();
assert_ok!(InboundQueue::process_message(
relayer,
Message {
nonce,
assets: vec![],
xcm: XcmPayload::Raw(vec![]),
claimer: None,
execution_fee: 1_000_000_000,
relayer_fee,
gateway: mock::GatewayAddress::get(),
origin: H160::random(),
value: 3_000_000_000,
},
));

// Relayer fee should be paid out even without tip
assert_eq!(
RegisteredRewardsCount::get(),
1,
"Relayer fee should be paid out even when no tip exists"
);

// Check the actual reward amount paid out
assert_eq!(
RegisteredRewardAmount::get(),
relayer_fee,
"Reward amount should equal relayer_fee when no tip exists"
);

// Confirm no tip storage was affected
assert_eq!(Tips::<Test>::get(nonce), None);
});
}

#[test]
fn tip_paid_out_when_no_relayer_fee() {
new_tester().execute_with(|| {
let nonce: u64 = 99;
let tip: u128 = 8_500;

// Add tip for nonce before message is processed
assert_ok!(InboundQueue::add_tip(nonce, tip));
assert_eq!(Tips::<Test>::get(nonce), Some(tip));

// Process inbound message with zero relayer_fee but with tip
let relayer: AccountId = Keyring::Bob.into();
assert_ok!(InboundQueue::process_message(
relayer,
Message {
nonce,
assets: vec![],
xcm: XcmPayload::Raw(vec![]),
claimer: None,
execution_fee: 1_000_000_000,
relayer_fee: 0,
gateway: mock::GatewayAddress::get(),
origin: H160::random(),
value: 3_000_000_000,
},
));

// Tip should be paid out even without relayer fee
assert_eq!(
RegisteredRewardsCount::get(),
1,
"Tip should be paid out even when relayer_fee is 0"
);

// Check the actual reward amount paid out (should be just the tip)
assert_eq!(
RegisteredRewardAmount::get(),
tip,
"Reward amount should equal tip when relayer_fee is 0"
);

// Tip should be consumed from storage
assert_eq!(Tips::<Test>::get(nonce), None);
});
}
4 changes: 3 additions & 1 deletion bridges/snowbridge/test-utils/src/mock_rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ impl From<BridgeReward> for RewardsAccountParams<u64> {

parameter_types! {
pub static RegisteredRewardsCount: u128 = 0;
pub static RegisteredRewardAmount: u128 = 0;
}

pub struct MockRewardLedger;
Expand All @@ -58,8 +59,9 @@ impl RewardLedger<sp_runtime::AccountId32, BridgeReward, u128> for MockRewardLed
fn register_reward(
_relayer: &sp_runtime::AccountId32,
_reward: BridgeReward,
_reward_balance: u128,
reward_balance: u128,
) {
RegisteredRewardsCount::set(RegisteredRewardsCount::get().saturating_add(1));
RegisteredRewardAmount::set(reward_balance);
}
}
13 changes: 13 additions & 0 deletions prdoc/pr_9746.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
title: Snowbridge Inbound Queue V2 relayer tip payout fix

doc:
- audience: Runtime Dev
description: |
Fixes a bug where relayer tips were not properly paid out, causing the tips to be lost since it had already been
burnt.

crates:
- name: snowbridge-pallet-inbound-queue-v2
bump: patch
- name: snowbridge-test-utils
bump: minor
Loading