Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
be27497
Update transfer token gas and fee
alistair-singh Mar 17, 2025
d92de78
add prdoc
alistair-singh Mar 17, 2025
87b03e1
Merge branch 'master' into alistair/transfer-token-gas
alistair-singh Mar 17, 2025
7a4bc44
revert mint
alistair-singh Mar 18, 2025
4a49ce0
update prdoc
alistair-singh Mar 18, 2025
c5d92d0
Update prdoc/pr_7947.prdoc
alistair-singh Mar 18, 2025
7f18465
Merge branch 'master' into alistair/transfer-token-gas
alistair-singh Mar 30, 2025
364145f
upgrade gas for v2
alistair-singh Apr 1, 2025
72a99c3
empty migration
alistair-singh Apr 1, 2025
2882fac
fix migration
alistair-singh Apr 8, 2025
d8a02dc
add storage version
alistair-singh Apr 8, 2025
4b1e42d
update pr doc
alistair-singh Apr 8, 2025
c7b6e53
Implement the halfing of fee per gas
alistair-singh Apr 8, 2025
8abfa3e
Fix weights
alistair-singh Apr 8, 2025
c2488ae
add post and pre migration checks
alistair-singh Apr 8, 2025
fd6a265
more accurate name
alistair-singh Apr 8, 2025
f994615
Merge branch 'master' into alistair/transfer-token-gas
alistair-singh Apr 8, 2025
d8b25e8
fix clippy
alistair-singh Apr 8, 2025
720a12e
fix semver
alistair-singh Apr 8, 2025
c760a58
copy paste error
alistair-singh Apr 8, 2025
cb25929
check v1 and v2
alistair-singh Apr 9, 2025
2725b03
updated comments
alistair-singh Apr 11, 2025
76ed37f
Merge branch 'master' into alistair/transfer-token-gas
alistair-singh Apr 11, 2025
083bd3b
Fix breaking test
yrong Apr 13, 2025
a51b21d
Merge pull request #1 from yrong/ron/transfer-token-gas
alistair-singh Apr 13, 2025
2f528f2
Merge branch 'master' into alistair/transfer-token-gas
alistair-singh Apr 13, 2025
f598dc8
fix pr doc
alistair-singh Apr 13, 2025
4248c8e
fix bridge hub westend tests
alistair-singh Apr 14, 2025
dd76151
Merge branch 'master' into alistair/transfer-token-gas
alistair-singh Apr 15, 2025
cf05cf4
Merge branch 'master' into alistair/transfer-token-gas
alistair-singh Apr 15, 2025
b186a68
Merge branch 'master' into alistair/transfer-token-gas
alistair-singh Apr 15, 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
2 changes: 1 addition & 1 deletion bridges/snowbridge/pallets/outbound-queue-v2/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ fn encode_unlock_message() {
let message: Message = mock_message(1000);
let message_abi_encoded = encode_mock_message(message);
println!("{}", HexDisplay::from(&message_abi_encoded));
assert_eq!(hex!("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000f4240").to_vec(), message_abi_encoded)
assert_eq!(hex!("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000f4240").to_vec(), message_abi_encoded)
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions bridges/snowbridge/pallets/system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub mod pallet {
use super::*;

#[pallet::pallet]
#[pallet::storage_version(migration::STORAGE_VERSION)]
pub struct Pallet<T>(_);

#[pallet::config]
Expand Down
159 changes: 153 additions & 6 deletions bridges/snowbridge/pallets/system/src/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,30 @@
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
//! Governance API for controlling the Ethereum side of the bridge
use super::*;
use frame_support::traits::OnRuntimeUpgrade;
use frame_support::{
migrations::VersionedMigration,
pallet_prelude::*,
traits::{OnRuntimeUpgrade, UncheckedOnRuntimeUpgrade},
weights::Weight,
};
use log;
use sp_std::marker::PhantomData;

#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;

pub mod v0 {
use frame_support::{pallet_prelude::*, weights::Weight};
const LOG_TARGET: &str = "ethereum_system::migration";

use super::*;
/// The in-code storage version.
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);

const LOG_TARGET: &str = "ethereum_system::migration";
pub mod v0 {
use super::*;

pub struct InitializeOnUpgrade<T, BridgeHubParaId, AssetHubParaId>(
sp_std::marker::PhantomData<(T, BridgeHubParaId, AssetHubParaId)>,
PhantomData<(T, BridgeHubParaId, AssetHubParaId)>,
);

impl<T, BridgeHubParaId, AssetHubParaId> OnRuntimeUpgrade
for InitializeOnUpgrade<T, BridgeHubParaId, AssetHubParaId>
where
Expand Down Expand Up @@ -72,3 +80,142 @@ pub mod v0 {
}
}
}

pub mod v1 {
use super::*;

#[cfg(feature = "try-runtime")]
use sp_core::U256;

/// Descreases the fee per gas.
pub struct FeePerGasMigration<T>(PhantomData<T>);

#[cfg(feature = "try-runtime")]
impl<T> FeePerGasMigration<T>
where
T: Config,
{
/// Calculate the fee required to pay for gas on Ethereum.
fn calculate_remote_fee_v1(params: &PricingParametersOf<T>) -> U256 {
use snowbridge_outbound_queue_primitives::v1::{
AgentExecuteCommand, Command, ConstantGasMeter, GasMeter,
};
let command = Command::AgentExecute {
agent_id: H256::zero(),
command: AgentExecuteCommand::TransferToken {
token: H160::zero(),
recipient: H160::zero(),
amount: 0,
},
};
let gas_used_at_most = ConstantGasMeter::maximum_gas_used_at_most(&command);
params
.fee_per_gas
.saturating_mul(gas_used_at_most.into())
.saturating_add(params.rewards.remote)
}

/// Calculate the fee required to pay for gas on Ethereum.
fn calculate_remote_fee_v2(params: &PricingParametersOf<T>) -> U256 {
use snowbridge_outbound_queue_primitives::v2::{Command, ConstantGasMeter, GasMeter};
let command = Command::UnlockNativeToken {
token: H160::zero(),
recipient: H160::zero(),
amount: 0,
};
let gas_used_at_most = ConstantGasMeter::maximum_dispatch_gas_used_at_most(&command);
params
.fee_per_gas
.saturating_mul(gas_used_at_most.into())
.saturating_add(params.rewards.remote)
}
}

/// The percentage gas increase. We must adjust the fee per gas by this percentage.
const GAS_INCREASE_PERCENTAGE: u64 = 70;

impl<T> UncheckedOnRuntimeUpgrade for FeePerGasMigration<T>
where
T: Config,
{
fn on_runtime_upgrade() -> Weight {
let mut params = Pallet::<T>::parameters();

let old_fee_per_gas = params.fee_per_gas;

// Fee per gas can be set based on a percentage in order to keep the remote fee the
// same.
params.fee_per_gas = params.fee_per_gas * GAS_INCREASE_PERCENTAGE / 100;

log::info!(
target: LOG_TARGET,
"Fee per gas migrated from {old_fee_per_gas:?} to {0:?}.",
params.fee_per_gas,
);

PricingParameters::<T>::put(params);
T::DbWeight::get().reads_writes(1, 1)
}

#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
use codec::Encode;

let params = Pallet::<T>::parameters();
let remote_fee_v1 = Self::calculate_remote_fee_v1(&params);
let remote_fee_v2 = Self::calculate_remote_fee_v2(&params);

log::info!(
target: LOG_TARGET,
"Pre fee per gas migration: pricing parameters = {params:?}, remote_fee_v1 = {remote_fee_v1:?}, remote_fee_v2 = {remote_fee_v2:?}"
);
Ok((params, remote_fee_v1, remote_fee_v2).encode())
}

#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
use codec::Decode;

let (old_params, old_remote_fee_v1, old_remote_fee_v2): (
PricingParametersOf<T>,
U256,
U256,
) = Decode::decode(&mut &state[..]).unwrap();

let params = Pallet::<T>::parameters();
ensure!(old_params.exchange_rate == params.exchange_rate, "Exchange rate unchanged.");
ensure!(old_params.rewards == params.rewards, "Rewards unchanged.");
ensure!(
(old_params.fee_per_gas * GAS_INCREASE_PERCENTAGE / 100) == params.fee_per_gas,
"Fee per gas decreased."
);
ensure!(old_params.multiplier == params.multiplier, "Multiplier unchanged.");

let remote_fee_v1 = Self::calculate_remote_fee_v1(&params);
let remote_fee_v2 = Self::calculate_remote_fee_v2(&params);
ensure!(
remote_fee_v1 <= old_remote_fee_v1,
"The v1 remote fee can cover the cost of the previous fee."
);
ensure!(
remote_fee_v2 <= old_remote_fee_v2,
"The v2 remote fee can cover the cost of the previous fee."
);

log::info!(
target: LOG_TARGET,
"Post fee per gas migration: pricing parameters = {params:?} remote_fee_v1 = {remote_fee_v1:?} remote_fee_v2 = {remote_fee_v2:?}"
);
Ok(())
}
}
}

/// Run the migration of the gas price and increment the pallet version so it cannot be re-run.
pub type FeePerGasMigrationV0ToV1<T> = VersionedMigration<
0,
1,
v1::FeePerGasMigration<T>,
Pallet<T>,
<T as frame_system::Config>::DbWeight,
>;
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ impl GasMeter for ConstantGasMeter {
// * No gas refund for clearing storage slot of source account in ERC20 contract
// * Assume dest account in ERC20 contract does not yet have a storage slot
// * ERC20.transferFrom possibly does other business logic besides updating balances
AgentExecuteCommand::TransferToken { .. } => 100_000,
AgentExecuteCommand::TransferToken { .. } => 200_000,
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these constants network-dependent? Could they possibly differ between the ETH testnet and production? Is it likely that we’ll change them again in the future?

I think it's not a common pattern to use versioned storage migration just for changing values rather than the structure of the storage.

I'm wondering if it would make sense for the ConstantGasMeter to be provided by the runtimes - in other words, extracted from the outbound-queue crate and placed directly into the runtimes.

That way, next time we wouldn’t need to touch the polkadot-sdk crates - we could just update the fellows runtimes. We can add migrations directly in the runtime. Overall, for changing fee/gas constants, we should only need to do a patch release of the fellows runtimes, without touching the polkadot-sdk crates.

Actuall, I think in this case, we could create ConstantGasMeter copy in fellows and add migrations directly there, so we are not blocked in this case.


I don’t want to change it in this PR, but previously we had a similar issue with the Electra stuff, where some configuration was hard-coded in polkadot-sdk when it should have been externalized.

My intention here is to externalize those hard-coded configurations and constants so they can be provided by the runtime. This way, we can update and release production runtimes more easily, without having to wait for a stable polkadot-sdk release - we can just make the changes directly when needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are these constants network-dependent?

No, this is the upper bound on gas usage for the action on the Ethereum side. And this is stable per runtime because the EVM is the same across bridge instances (paseo-sepolia, westend-sepolia, polkadot-mainnet) and the same contract will consume the same amount of gas regardless of the Ethereum chain. The gas price or fee_per_gas can change per bridge instance but that is stored in pallet storage and can be updated with set_pricing_parameters extrinsic.

},
Command::Upgrade { initializer, .. } => {
let initializer_max_gas = match *initializer {
Expand All @@ -362,7 +362,7 @@ impl GasMeter for ConstantGasMeter {
},
Command::SetTokenTransferFees { .. } => 60_000,
Command::SetPricingParameters { .. } => 60_000,
Command::UnlockNativeToken { .. } => 100_000,
Command::UnlockNativeToken { .. } => 200_000,
Command::RegisterForeignToken { .. } => 1_200_000,
Command::MintForeignToken { .. } => 100_000,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ impl GasMeter for ConstantGasMeter {
// the the initializer is called.
50_000 + initializer.maximum_required_gas
},
Command::UnlockNativeToken { .. } => 100_000,
Command::UnlockNativeToken { .. } => 200_000,
Command::RegisterForeignToken { .. } => 1_200_000,
Command::MintForeignToken { .. } => 100_000,
Command::CallContract { gas: gas_limit, .. } => *gas_limit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const INSUFFICIENT_XCM_FEE: u128 = 1000;
const TOKEN_AMOUNT: u128 = 100_000_000_000;
const TREASURY_ACCOUNT: [u8; 32] =
hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000");
const BRIDGE_FEE: u128 = 4_000_000_000_000;

#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
pub enum ControlCall {
Expand Down Expand Up @@ -1347,7 +1348,7 @@ fn transfer_penpal_native_asset() {
// DOT as fee
let assets = vec![
// Should cover the bridge fee
Asset { id: AssetId(Location::parent()), fun: Fungible(3_000_000_000_000) },
Asset { id: AssetId(Location::parent()), fun: Fungible(BRIDGE_FEE) },
Asset { id: AssetId(Location::here()), fun: Fungible(TOKEN_AMOUNT) },
];

Expand Down Expand Up @@ -1555,7 +1556,7 @@ fn transfer_penpal_teleport_enabled_asset() {
// DOT as fee
let assets = vec![
// Should cover the bridge fee
Asset { id: AssetId(Location::parent()), fun: Fungible(3_000_000_000_000) },
Asset { id: AssetId(Location::parent()), fun: Fungible(BRIDGE_FEE) },
Asset { id: AssetId(asset_location_on_penpal.clone()), fun: Fungible(TOKEN_AMOUNT) },
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ pub mod bridging {
/// (initially was calculated by test `OutboundQueue::calculate_fees` - ETH/ROC 1/400 and fee_per_gas 20 GWEI = 2200698000000 + *25%)
/// Needs to be more than fee calculated from DefaultFeeConfig FeeConfigRecord in snowbridge:parachain/pallets/outbound-queue/src/lib.rs
/// Polkadot uses 10 decimals, Kusama and Rococo 12 decimals.
pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000;
pub const DefaultBridgeHubEthereumBaseFee: Balance = 3_833_568_200_000;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could probably add the value here and then reuse: https://github.com/paritytech/polkadot-sdk/blob/master/cumulus/parachains/runtimes/constants/src/westend.rs#L172 Not a major issue though

pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get();
pub SiblingBridgeHubWithEthereumInboundQueueInstance: Location = Location::new(
1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ pub mod bridging {
/// (initially was calculated by test `OutboundQueue::calculate_fees` - ETH/WND 1/400 and fee_per_gas 20 GWEI = 2200698000000 + *25%)
/// Needs to be more than fee calculated from DefaultFeeConfig FeeConfigRecord in snowbridge:parachain/pallets/outbound-queue/src/lib.rs
/// Polkadot uses 10 decimals, Kusama,Rococo,Westend 12 decimals.
pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000;
pub const DefaultBridgeHubEthereumBaseFee: Balance = 3_833_568_200_000;
pub const DefaultBridgeHubEthereumBaseFeeV2: Balance = 100_000_000_000;
pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get();
pub storage BridgeHubEthereumBaseFeeV2: Balance = DefaultBridgeHubEthereumBaseFeeV2::get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ pub type Migrations = (
ConstU32<BRIDGE_HUB_ID>,
ConstU32<ASSET_HUB_ID>,
>,
snowbridge_pallet_system::migration::FeePerGasMigrationV0ToV1<Runtime>,
pallet_bridge_messages::migration::v1::MigrationToV1<
Runtime,
bridge_to_westend_config::WithBridgeHubWestendMessagesInstance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use sp_runtime::{
};

parameter_types! {
pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000;
pub const DefaultBridgeHubEthereumBaseFee: Balance = 3_833_568_200_000;
}

fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys<Runtime> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ pub type Migrations = (
ConstU32<BRIDGE_HUB_ID>,
ConstU32<ASSET_HUB_ID>,
>,
snowbridge_pallet_system::migration::FeePerGasMigrationV0ToV1<Runtime>,
bridge_to_ethereum_config::migrations::MigrationForXcmV5<Runtime>,
pallet_session::migrations::v1::MigrateV0ToV1<
Runtime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use sp_runtime::{
};

parameter_types! {
pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000;
pub const DefaultBridgeHubEthereumBaseFee: Balance = 3_833_568_200_000;
}

fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys<Runtime> {
Expand Down
24 changes: 24 additions & 0 deletions prdoc/pr_7947.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
title: Snowbridge - Update TransferToken command gas limit.

doc:
- audience: Runtime Dev
description: |
Transfering certain ERC20 tokens require more gas than 100_000 gas. An example is LDO token which requires 140_000 gas.
This change updates the gas limit to 200_000 and also updates the default fees for testnet runtimes.
NOTE: make sure to update the relevant runtime fees to account for this change.

crates:
- name: asset-hub-westend-runtime
bump: patch
- name: asset-hub-rococo-runtime
bump: patch
- name: bridge-hub-westend-runtime
bump: patch
- name: bridge-hub-rococo-runtime
bump: patch
- name: snowbridge-outbound-queue-primitives
bump: patch
- name: snowbridge-pallet-system
bump: minor
- name: snowbridge-pallet-outbound-queue-v2
bump: minor
Loading