Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add command to update registerTokenFee and sendTokenFee #991

Merged
merged 15 commits into from
Nov 9, 2023
Merged
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}))
Copy link
Collaborator

Choose a reason for hiding this comment

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

The default fees are already (1, 1), so this test could pass even if the command failed somehow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually the default are 1(ether) not 1(wei), anyway check fee before and after set and make sure change as expected.
0a2cb16

);
(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 {
claravanstaden marked this conversation as resolved.
Show resolved Hide resolved
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,
Comment on lines +444 to +445
Copy link
Collaborator

Choose a reason for hiding this comment

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

Lets validate these to make sure they are non-zero, and if not, return an InvalidTokenFees error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

) -> 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
Loading