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

Commit

Permalink
Add command to update registerTokenFee and sendTokenFee (paritytech#991)
Browse files Browse the repository at this point in the history
* Add command to update registerTokenFee and sendTokenFee

* Add smoke test for update fees

* Rename to setTokenTransferFees

* Improve for the comments

* Update sdk

* Update parachain/pallets/control/src/lib.rs

Co-authored-by: Clara van Staden <claravanstaden64@gmail.com>

* Update parachain/pallets/control/src/lib.rs

Co-authored-by: Clara van Staden <claravanstaden64@gmail.com>

* Rename as InvalidTokenTransferFees

---------

Co-authored-by: Clara van Staden <claravanstaden64@gmail.com>
  • Loading branch information
yrong and claravanstaden authored Nov 9, 2023
1 parent df8d5da commit 654f242
Show file tree
Hide file tree
Showing 14 changed files with 340 additions and 25 deletions.
26 changes: 26 additions & 0 deletions contracts/src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ contract Gateway is IGateway, IInitializable {
catch {
success = false;
}
} else if (message.command == Command.SetTokenTransferFees) {
try Gateway(this).setTokenTransferFees{gas: maxDispatchGas}(message.params) {}
catch {
success = false;
}
}

// Calculate the actual cost of executing this message
Expand Down Expand Up @@ -230,6 +235,11 @@ contract Gateway is IGateway, IInitializable {
return ERC1967.load();
}

function tokenTransferFees() external view returns (uint256, uint256) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();
return ($.registerTokenFee, $.sendTokenFee);
}

/**
* Handlers
*/
Expand Down Expand Up @@ -411,6 +421,22 @@ contract Gateway is IGateway, IInitializable {
emit AgentFundsWithdrawn(params.agentID, params.recipient, params.amount);
}

struct SetTokenTransferFeesParams {
/// @dev The fee for register token
uint256 register;
/// @dev The fee for send token from ethereum to polkadot
uint256 send;
}

// @dev Set the operating mode of the gateway
function setTokenTransferFees(bytes calldata data) external onlySelf {
AssetsStorage.Layout storage $ = AssetsStorage.layout();
SetTokenTransferFeesParams memory params = abi.decode(data, (SetTokenTransferFeesParams));
$.registerTokenFee = params.register;
$.sendTokenFee = params.send;
emit TokenTransferFeesChanged(params.register, params.send);
}

/**
* Assets
*/
Expand Down
3 changes: 2 additions & 1 deletion contracts/src/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ enum Command {
CreateChannel,
UpdateChannel,
SetOperatingMode,
TransferNativeFromAgent
TransferNativeFromAgent,
SetTokenTransferFees
}

enum AgentExecuteCommand {TransferToken}
3 changes: 3 additions & 0 deletions contracts/src/interfaces/IGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ interface IGateway {
// Emitted when funds are withdrawn from an agent
event AgentFundsWithdrawn(bytes32 indexed agentID, address indexed recipient, uint256 amount);

// Emitted when the fees updated
event TokenTransferFeesChanged(uint256 register, uint256 send);
/// @dev Emitted once the funds are locked and a message is successfully queued.
event TokenSent(
address indexed token, address indexed sender, ParaID destinationChain, bytes destinationAddress, uint128 amount
Expand Down Expand Up @@ -65,6 +67,7 @@ interface IGateway {
/**
* Token Transfers
*/
function tokenTransferFees() external view returns (uint256, uint256);

/// @dev Send a message to the AssetHub parachain to register a new fungible asset
/// in the `ForeignAssets` pallet.
Expand Down
12 changes: 12 additions & 0 deletions contracts/test/Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -749,4 +749,16 @@ contract GatewayTest is Test {
InboundMessage(bridgeHubParaID, 1, command, params, 1, maxRefund, reward), proof, makeMockProof()
);
}

function testSetTokenFees() public {
(uint256 register, uint256 send) = IGateway(address(gateway)).tokenTransferFees();
assertEq(register, 1 ether);
assertEq(send, 1 ether);
GatewayMock(address(gateway)).setTokenTransferFeesPublic(
abi.encode(Gateway.SetTokenTransferFeesParams({register: 1, send: 1}))
);
(register, send) = IGateway(address(gateway)).tokenTransferFees();
assertEq(register, 1);
assertEq(send, 1);
}
}
4 changes: 4 additions & 0 deletions contracts/test/mocks/GatewayMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ contract GatewayMock is Gateway {
return commitmentsAreVerified;
}
}

function setTokenTransferFeesPublic(bytes calldata params) external {
this.setTokenTransferFees(params);
}
}

library AdditionalStorage {
Expand Down
4 changes: 4 additions & 0 deletions contracts/test/mocks/GatewayUpgradeMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ contract GatewayUpgradeMock is IGateway, IInitializable {
return address(0);
}

function tokenTransferFees() external pure returns (uint256, uint256) {
return (1, 1);
}

function implementation() external pure returns (address) {
return address(0);
}
Expand Down
30 changes: 30 additions & 0 deletions parachain/pallets/control/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ pub mod pallet {
SetOperatingMode { mode: OperatingMode },
/// An TransferNativeFromAgent message was sent to the Gateway
TransferNativeFromAgent { agent_id: AgentId, recipient: H160, amount: u128 },
/// A SetTokenTransferFees message was sent to the Gateway
SetTokenTransferFees { register: u128, send: u128 },
}

#[pallet::error]
Expand All @@ -188,6 +190,7 @@ pub mod pallet {
UnsupportedLocationVersion,
InvalidLocation,
Send(SendError),
InvalidTokenTransferFees,
}

/// The set of registered agents
Expand Down Expand Up @@ -424,6 +427,33 @@ pub mod pallet {

Self::do_transfer_native_from_agent(agent_id, para_id, recipient, amount, pays_fee)
}

/// Sends a message to the Gateway contract to set token transfer fees
///
/// Privileged. Can only be called by root.
///
/// Fee required: No
///
/// - `origin`: Must be root
/// - `register`: The fee for register token
/// - `send`: The fee for send token to parachain
#[pallet::call_index(8)]
#[pallet::weight(T::WeightInfo::set_token_transfer_fees())]
pub fn set_token_transfer_fees(
origin: OriginFor<T>,
register: u128,
send: u128,
) -> DispatchResult {
ensure_root(origin)?;

ensure!(register > 0 && send > 0, Error::<T>::InvalidTokenTransferFees);

let command = Command::SetTokenTransferFees { register, send };
Self::send(T::OwnParaId::get(), command, PaysFee::<T>::No)?;

Self::deposit_event(Event::<T>::SetTokenTransferFees { register, send });
Ok(())
}
}

impl<T: Config> Pallet<T> {
Expand Down
21 changes: 21 additions & 0 deletions parachain/pallets/control/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub trait WeightInfo {
fn set_operating_mode() -> Weight;
fn transfer_native_from_agent() -> Weight;
fn force_transfer_native_from_agent() -> Weight;
fn set_token_transfer_fees() -> Weight;
}

// For backwards compatibility and tests.
Expand Down Expand Up @@ -204,4 +205,24 @@ impl WeightInfo for () {
.saturating_add(RocksDbWeight::get().reads(5_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}

/// Storage: ParachainInfo ParachainId (r:1 w:0)
/// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0)
/// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen)
/// Storage: MessageQueue BookStateFor (r:1 w:1)
/// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen)
/// Storage: MessageQueue ServiceHead (r:1 w:1)
/// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen)
/// Storage: MessageQueue Pages (r:0 w:1)
/// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen)
fn set_token_transfer_fees() -> Weight {
// Proof Size summary in bytes:
// Measured: `80`
// Estimated: `3517`
// Minimum execution time: 31_000_000 picoseconds.
Weight::from_parts(42_000_000, 3517)
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}
}
14 changes: 14 additions & 0 deletions parachain/primitives/core/src/outbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ mod v1 {
/// The amount to transfer
amount: u128,
},
/// Set token fees of the Gateway contract
SetTokenTransferFees {
/// The fee for register token
register: u128,
/// The fee for send token to para chain
send: u128,
},
}

impl Command {
Expand All @@ -124,6 +131,7 @@ mod v1 {
Command::UpdateChannel { .. } => 4,
Command::SetOperatingMode { .. } => 5,
Command::TransferNativeFromAgent { .. } => 6,
Command::SetTokenTransferFees { .. } => 7,
}
}

Expand Down Expand Up @@ -170,6 +178,11 @@ mod v1 {
Token::Address(*recipient),
Token::Uint(U256::from(*amount)),
])]),
Command::SetTokenTransferFees { register, send } =>
ethabi::encode(&[Token::Tuple(vec![
Token::Uint(U256::from(*register)),
Token::Uint(U256::from(*send)),
])]),
}
}
}
Expand Down Expand Up @@ -335,6 +348,7 @@ impl GasMeter for ConstantGasMeter {
// the the initializer is called.
50_000 + initializer_max_gas
},
Command::SetTokenTransferFees { .. } => 60_000,
}
}
}
Expand Down
169 changes: 168 additions & 1 deletion relayer/contracts/gateway.go

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions smoketest/src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,7 @@ pub async fn governance_bridgehub_call_from_relay_chain(
}));
let message = Box::new(RelaychainVersionedXcm::V3(RelaychainXcm(vec![
RelaychainInstruction::UnpaidExecution {
weight_limit: RelaychainWeightLimit::Limited(RelaychainWeight {
ref_time: weight,
proof_size,
}),
weight_limit: RelaychainWeightLimit::Unlimited,
check_origin: None,
},
RelaychainInstruction::Transact {
Expand Down
39 changes: 39 additions & 0 deletions smoketest/tests/set_token_transfer_fees.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use ethers::prelude::Address;
use snowbridge_smoketest::{
constants::*,
contracts::{i_gateway, i_gateway::SetTokenTransferFeesFilter},
helper::*,
parachains::bridgehub::api::{
ethereum_control::events::SetTokenTransferFees,
runtime_types::{self, bridge_hub_rococo_runtime::RuntimeCall as BHRuntimeCall},
},
};

#[tokio::test]
async fn set_token_transfer_fees() {
let test_clients = initial_clients().await.expect("initialize clients");

let gateway_addr: Address = GATEWAY_PROXY_CONTRACT.into();
let ethereum_client = *(test_clients.ethereum_client.clone());
let gateway = i_gateway::IGateway::new(gateway_addr, ethereum_client.clone());
let fees = gateway.token_transfer_fees().await.expect("get fees");
println!("asset fees {:?}", fees);

let set_token_fees_call = BHRuntimeCall::EthereumControl(
runtime_types::snowbridge_control::pallet::Call::set_token_transfer_fees {
register: 10_000_000_000_000,
send: 20_000_000_000,
},
);

governance_bridgehub_call_from_relay_chain(vec![set_token_fees_call])
.await
.expect("set token fees");

wait_for_bridgehub_event::<SetTokenTransferFees>(&test_clients.bridge_hub_client).await;

wait_for_ethereum_event::<SetTokenTransferFeesFilter>(&test_clients.ethereum_client).await;

let fees = gateway.token_transfer_fees().await.expect("get fees");
println!("asset fees {:?}", fees);
}
33 changes: 15 additions & 18 deletions smoketest/tests/transfer_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,31 +60,28 @@ async fn transfer_token() {
let signer: PairSigner<AssetHubConfig, _> = PairSigner::new(keypair);

let amount: u128 = 1_000_000_000;
let assets = VersionedMultiAssets::V3(MultiAssets(vec![
MultiAsset {
id: AssetId::Concrete(MultiLocation {
parents: 2,
interior: Junctions::X2(
Junction::GlobalConsensus(NetworkId::Ethereum { chain_id: 15 }),
Junction::AccountKey20 { network: None, key: WETH_CONTRACT.into() },
),
}),
fun: Fungibility::Fungible(amount),
},
]));
let assets = VersionedMultiAssets::V3(MultiAssets(vec![MultiAsset {
id: AssetId::Concrete(MultiLocation {
parents: 2,
interior: Junctions::X2(
Junction::GlobalConsensus(NetworkId::Ethereum { chain_id: 15 }),
Junction::AccountKey20 { network: None, key: WETH_CONTRACT.into() },
),
}),
fun: Fungibility::Fungible(amount),
}]));

let destination = VersionedMultiLocation::V3(MultiLocation {
parents: 2,
interior: Junctions::X1(
Junction::GlobalConsensus(NetworkId::Ethereum { chain_id: 15 }),
),
interior: Junctions::X1(Junction::GlobalConsensus(NetworkId::Ethereum { chain_id: 15 })),
});

let beneficiary = VersionedMultiLocation::V3(MultiLocation {
parents: 0,
interior: Junctions::X1(
Junction::AccountKey20 { network: None, key: DESTINATION_ADDRESS.into() },
),
interior: Junctions::X1(Junction::AccountKey20 {
network: None,
key: DESTINATION_ADDRESS.into(),
}),
});

let token_transfer_call =
Expand Down

0 comments on commit 654f242

Please sign in to comment.