diff --git a/.changelog/unreleased/features/ibc-relayer/2566-extension-options.md b/.changelog/unreleased/features/ibc-relayer/2566-extension-options.md new file mode 100644 index 0000000000..407ca7f72b --- /dev/null +++ b/.changelog/unreleased/features/ibc-relayer/2566-extension-options.md @@ -0,0 +1,2 @@ +- Support custom extension options to be able to specify `max_priority_price` for Ethermint dynamic tx fee + ([#2566](https://github.com/informalsystems/ibc-rs/issues/2566)) diff --git a/relayer/src/chain/cosmos/encode.rs b/relayer/src/chain/cosmos/encode.rs index 416bc049d0..d804c7adc9 100644 --- a/relayer/src/chain/cosmos/encode.rs +++ b/relayer/src/chain/cosmos/encode.rs @@ -45,7 +45,8 @@ pub fn sign_tx( let signer = encode_signer_info(&config.address_type, account.sequence, key_bytes)?; - let (body, body_bytes) = tx_body_and_bytes(messages, tx_memo)?; + let (body, body_bytes) = + tx_body_and_bytes(messages, tx_memo, config.extension_options.clone())?; let (auth_info, auth_info_bytes) = auth_info_and_bytes(signer, fee.clone())?; @@ -159,13 +160,17 @@ fn auth_info_and_bytes(signer_info: SignerInfo, fee: Fee) -> Result<(AuthInfo, V Ok((auth_info, auth_buf)) } -fn tx_body_and_bytes(proto_msgs: Vec, memo: &Memo) -> Result<(TxBody, Vec), Error> { +fn tx_body_and_bytes( + proto_msgs: Vec, + memo: &Memo, + extension_options: Vec, +) -> Result<(TxBody, Vec), Error> { // Create TxBody let body = TxBody { messages: proto_msgs.to_vec(), memo: memo.to_string(), timeout_height: 0_u64, - extension_options: Vec::::new(), + extension_options, non_critical_extension_options: Vec::::new(), }; diff --git a/relayer/src/chain/cosmos/types/config.rs b/relayer/src/chain/cosmos/types/config.rs index fa2f924d2d..eda8a8716d 100644 --- a/relayer/src/chain/cosmos/types/config.rs +++ b/relayer/src/chain/cosmos/types/config.rs @@ -2,6 +2,7 @@ use core::str::FromStr; use core::time::Duration; use http::Uri; use ibc::core::ics24_host::identifier::ChainId; +use ibc_proto::google::protobuf::Any; use tendermint_rpc::{HttpClient, Url}; use crate::chain::cosmos::types::gas::GasConfig; @@ -17,6 +18,7 @@ pub struct TxConfig { pub grpc_address: Uri, pub rpc_timeout: Duration, pub address_type: AddressType, + pub extension_options: Vec, } impl<'a> TryFrom<&'a ChainConfig> for TxConfig { @@ -31,6 +33,12 @@ impl<'a> TryFrom<&'a ChainConfig> for TxConfig { let gas_config = GasConfig::from(config); + let extension_options = config + .extension_options + .iter() + .map(|opt| opt.to_any()) + .collect::>()?; + Ok(Self { chain_id: config.id.clone(), gas_config, @@ -39,6 +47,7 @@ impl<'a> TryFrom<&'a ChainConfig> for TxConfig { grpc_address, rpc_timeout: config.rpc_timeout, address_type: config.address_type.clone(), + extension_options, }) } } diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index 9b7eee2775..c08e14a88d 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -515,6 +515,7 @@ pub mod test_utils { address_type: AddressType::default(), memo_prefix: Default::default(), proof_specs: Default::default(), + extension_options: Default::default(), sequential_batch_tx: false, } } diff --git a/relayer/src/config.rs b/relayer/src/config.rs index 022656e33a..0dad2b4c5f 100644 --- a/relayer/src/config.rs +++ b/relayer/src/config.rs @@ -9,6 +9,7 @@ use alloc::collections::BTreeMap; use core::{fmt, time::Duration}; use std::{fs, fs::File, io::Write, path::Path}; +use ibc_proto::google::protobuf::Any; use serde_derive::{Deserialize, Serialize}; use tendermint_light_client_verifier::types::TrustThreshold; @@ -18,6 +19,8 @@ use ibc::timestamp::ZERO_DURATION; use crate::chain::ChainType; use crate::config::types::{MaxMsgNum, MaxTxSize, Memo}; +use crate::error::Error as RelayerError; +use crate::extension_options::ExtensionOptionDynamicFeeTx; use crate::keyring::Store; pub use error::Error; @@ -42,6 +45,42 @@ impl fmt::Display for GasPrice { } } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde( + rename_all = "snake_case", + tag = "type", + content = "value", + deny_unknown_fields +)] +pub enum ExtensionOption { + EthermintDynamicFee(String), +} + +impl ExtensionOption { + pub fn to_any(&self) -> Result { + match self { + Self::EthermintDynamicFee(max_priority_price) => ExtensionOptionDynamicFeeTx { + max_priority_price: max_priority_price.into(), + } + .to_any(), + } + } +} + +impl fmt::Display for ExtensionOption { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::EthermintDynamicFee(max_priority_price) => { + write!( + f, + "EthermintDynamicFee(max_priority_price: {})", + max_priority_price + ) + } + } + } +} + /// Defaults for various fields pub mod default { use super::*; @@ -383,6 +422,8 @@ pub struct ChainConfig { pub packet_filter: PacketFilter, #[serde(default)] pub address_type: AddressType, + #[serde(default = "Vec::new", skip_serializing_if = "Vec::is_empty")] + pub extension_options: Vec, } /// Attempt to load and parse the TOML config file as a `Config`. diff --git a/relayer/src/extension_options.rs b/relayer/src/extension_options.rs new file mode 100644 index 0000000000..b796a5cbbe --- /dev/null +++ b/relayer/src/extension_options.rs @@ -0,0 +1,25 @@ +use ibc_proto::google::protobuf::Any; +use prost::Message; +use serde_derive::{Deserialize, Serialize}; + +use crate::error::Error; + +// ExtensionOptionDynamicFeeTx is an extension option used with ethermint dynamic fee tx. +// protobuf message: https://github.com/evmos/ethermint/blob/main/proto/ethermint/types/v1/dynamic_fee.proto +#[derive(Clone, PartialEq, Eq, Message, Serialize, Deserialize)] +pub struct ExtensionOptionDynamicFeeTx { + #[prost(string, tag = "1")] + pub max_priority_price: ::prost::alloc::string::String, +} + +impl ExtensionOptionDynamicFeeTx { + pub fn to_any(&self) -> Result { + let mut buf = Vec::new(); + Message::encode(self, &mut buf) + .map_err(|e| Error::protobuf_encode("ExtensionOptionDynamicFeeTx".into(), e))?; + Ok(Any { + type_url: "/ethermint.types.v1.ExtensionOptionDynamicFeeTx".to_string(), + value: buf, + }) + } +} diff --git a/relayer/src/lib.rs b/relayer/src/lib.rs index c448542d54..0d62339d1e 100644 --- a/relayer/src/lib.rs +++ b/relayer/src/lib.rs @@ -29,6 +29,7 @@ pub mod connection; pub mod denom; pub mod error; pub mod event; +pub mod extension_options; pub mod foreign_client; pub mod keyring; pub mod light_client; diff --git a/tools/test-framework/src/relayer/tx.rs b/tools/test-framework/src/relayer/tx.rs index 1cce4ca4f3..c54d9e9bb6 100644 --- a/tools/test-framework/src/relayer/tx.rs +++ b/tools/test-framework/src/relayer/tx.rs @@ -61,6 +61,8 @@ pub fn new_tx_config_for_test( let address_type = Default::default(); + let extension_options = Default::default(); + Ok(TxConfig { chain_id, gas_config, @@ -69,6 +71,7 @@ pub fn new_tx_config_for_test( grpc_address, rpc_timeout, address_type, + extension_options, }) } diff --git a/tools/test-framework/src/types/single/node.rs b/tools/test-framework/src/types/single/node.rs index bc76275df0..25635a3c32 100644 --- a/tools/test-framework/src/types/single/node.rs +++ b/tools/test-framework/src/types/single/node.rs @@ -151,6 +151,7 @@ impl FullNode { address_type: Default::default(), memo_prefix: Default::default(), proof_specs: Default::default(), + extension_options: Default::default(), sequential_batch_tx: false, }) }