Skip to content

Commit

Permalink
Implement dynamic gas fee for Cosmos (#471)
Browse files Browse the repository at this point in the history
* Add memo to ibc_token_transfer

* Add GasToFeeConverter to chose between static and dynamic gas fee

* Move CanConvertGasToFee to cosmos_chain_components::traits

* Extract Hermes v1 gas_amount_to_fee in Hermes SDK

* Improve gas to fee converter for Cosmos

* Fix issue after merging main

* Fix LegacyCosmosBootstrap fields in cosmos_integration_tests_legacy

* Implement Dynamic Gas Component and add it as field of Bootstrap for tests

* Implement CanQueryEipBaseFee component

* Remove InConverter from DynamicConvertCosmosGasToFee

* Improve dynamic gas fee computation

* Update crates/cosmos/cosmos-chain-components/src/traits/eip_query.rs

Co-authored-by: Soares Chen <[email protected]>

* Wire EipQuerierComponent

* Extract Dynamic Gas Configuration for tests

* Remove hardcoded EIP endpoint

* Add DispatchQueryEip implementation

* Set FeeMarket as default EIP query type

* Move structs used for EIP query to their own file

* Remove unnecessary CanRaiseError

* Add default implementation for DynamicGasConfig

* Use ReturnNoDynamicGas for LegacyCosmosBootstrap

* cargo fmt

* Move EIP query type in Dynamic Gas Config

* Remove dependecy from Hermes v1 TxConfig

* Remove unnecessary dependencies from DispatchQueryEip

* Remove unnecessary dependencies from StaticConvertCosmosGasToFee and DynamicConvertCosmosGasToFee

* Fix FeeMarket url

* Disable dynamic gas fees for simd in WASM tests

* Fix dynamic gas configuration in bootstrap

* Disable dynamic gas fees for both_wasm_cosmos test

* Remove enabled field from DynamicGasConfig

* Remove U128 and Decimal struct used for dynamic gas

* Remove dynamic_gas field from LegacyCosmosBootstrap

---------

Co-authored-by: Soares Chen <[email protected]>
  • Loading branch information
ljoss17 and soareschen authored Nov 19, 2024
1 parent 7f4e3d6 commit cb2d629
Show file tree
Hide file tree
Showing 54 changed files with 1,223 additions and 61 deletions.
284 changes: 252 additions & 32 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,11 @@ clap = { version = "4.5.20" }
oneline-eyre = { version = "0.1.0" }
prost = { version = "0.13.3" }
prost-types = { version = "0.13.3" }
reqwest = { version = "0.12.9", features = ["json"] }
serde = { version = "1.0.210" }
serde_derive = { version = "1.0" }
serde_json = { version = "1.0" }
subtle-encoding = { version = "0.5.1" }
tokio = { version = "1.40" }
tracing = { version = "0.1.40" }
tracing-subscriber = { version = "0.3.18" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use alloc::sync::Arc;
use cgp::core::component::UseContext;
use hermes_cosmos_chain_components::types::config::gas::dynamic_gas_config::DynamicGasConfig;
use hermes_cosmos_test_components::bootstrap::impls::modifiers::no_modify_comet_config::NoModifyCometConfig;
use hermes_cosmos_test_components::bootstrap::impls::modifiers::no_modify_cosmos_sdk_config::NoModifyCosmosSdkConfig;
use hermes_cosmos_test_components::bootstrap::impls::modifiers::no_modify_genesis_config::NoModifyGenesisConfig;
use hermes_cosmos_test_components::bootstrap::traits::fields::dynamic_gas_fee::DynamicGasGetterComponent;
use hermes_cosmos_test_components::bootstrap::traits::modifiers::modify_cosmos_sdk_config::CosmosSdkConfigModifierComponent;
use std::path::PathBuf;
use std::sync::OnceLock;
Expand Down Expand Up @@ -64,6 +66,7 @@ pub struct CelestiaBootstrap {
pub cosmos_builder: Arc<CosmosBuilder>,
pub chain_store_dir: PathBuf,
pub bridge_store_dir: PathBuf,
pub dynamic_gas: Option<DynamicGasConfig>,
}

impl CanUseLegacyCosmosSdkChainBootstrapper for CelestiaBootstrap {}
Expand Down Expand Up @@ -110,6 +113,7 @@ delegate_components! {
[
ChainStoreDirGetterComponent,
CosmosBuilderGetterComponent,
DynamicGasGetterComponent,
]:
UseContext,
RandomIdFlagGetterComponent:
Expand Down
2 changes: 2 additions & 0 deletions crates/celestia/celestia-integration-tests/tests/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use eyre::Error;
use hermes_celestia_integration_tests::contexts::bootstrap::CelestiaBootstrap;
use hermes_celestia_test_components::bootstrap::traits::bootstrap_bridge::CanBootstrapBridge;
use hermes_cosmos_chain_components::types::config::gas::dynamic_gas_config::DynamicGasConfig;
use hermes_cosmos_relayer::contexts::build::CosmosBuilder;
use hermes_runtime::types::runtime::HermesRuntime;
use hermes_test_components::bootstrap::traits::chain::CanBootstrapChain;
Expand All @@ -30,6 +31,7 @@ fn test_celestia_bootstrap() -> Result<(), Error> {
cosmos_builder: builder.clone(),
chain_store_dir: store_dir.join("chains"),
bridge_store_dir: store_dir.join("bridges"),
dynamic_gas: Some(DynamicGasConfig::default()),
};

tokio_runtime.block_on(async move {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use core::time::Duration;
use std::sync::Arc;

use hermes_cosmos_chain_components::types::config::gas::dynamic_gas_config::DynamicGasConfig;
use hermes_cosmos_integration_tests::contexts::binary_channel::setup::CosmosBinaryChannelSetup;
use hermes_cosmos_integration_tests::contexts::bootstrap::CosmosBootstrap;
use hermes_cosmos_integration_tests::init::init_test_runtime;
Expand Down Expand Up @@ -35,6 +36,7 @@ fn celestia_integration_tests() -> Result<(), Error> {
transfer_denom_prefix: "coin".into(),
genesis_config_modifier: Box::new(|_| Ok(())),
comet_config_modifier: Box::new(|_| Ok(())),
dynamic_gas: Some(DynamicGasConfig::default()),
});

let cosmos_bootstrap = Arc::new(CosmosBootstrap {
Expand All @@ -48,6 +50,7 @@ fn celestia_integration_tests() -> Result<(), Error> {
transfer_denom_prefix: "coin".into(),
genesis_config_modifier: Box::new(|_| Ok(())),
comet_config_modifier: Box::new(|_| Ok(())),
dynamic_gas: Some(DynamicGasConfig::default()),
});

let create_client_settings = Settings {
Expand Down
1 change: 1 addition & 0 deletions crates/cli/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ hermes-relayer-components = { workspace = true }
hermes-cosmos-relayer = { workspace = true }
hermes-cosmos-integration-tests = { workspace = true }
hermes-cosmos-chain-components = { workspace = true }
hermes-cosmos-test-components = { workspace = true }

ibc = { workspace = true }
ibc-relayer = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions crates/cli/cli/src/commands/bootstrap/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use alloc::sync::Arc;

use cgp::prelude::*;
use hermes_cli_components::traits::bootstrap::{BootstrapLoader, HasBootstrapType};
use hermes_cosmos_chain_components::types::config::gas::dynamic_gas_config::DynamicGasConfig;
use hermes_cosmos_integration_tests::contexts::bootstrap::CosmosBootstrap;
use hermes_cosmos_relayer::contexts::build::CosmosBuilder;
use hermes_error::types::HermesError;
Expand Down Expand Up @@ -56,6 +57,7 @@ where
transfer_denom_prefix: args.transfer_denom.clone(),
genesis_config_modifier: Box::new(|_| Ok(())),
comet_config_modifier: Box::new(|_| Ok(())),
dynamic_gas: Some(DynamicGasConfig::default()),
};

Ok(bootstrap)
Expand Down
4 changes: 4 additions & 0 deletions crates/cosmos/cosmos-chain-components/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ futures = { workspace = true }
tracing = { workspace = true }
http = { workspace = true }
bech32 = { workspace = true }
reqwest = { workspace = true }
time = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tonic = { workspace = true, features = ["tls", "tls-roots"] }

subtle-encoding = { workspace = true }
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub use hermes_relayer_components::transaction::traits::types::transaction::Tran
pub use hermes_relayer_components::transaction::traits::types::tx_hash::TransactionHashTypeComponent;
pub use hermes_relayer_components::transaction::traits::types::tx_response::TxResponseTypeComponent;

pub use crate::impls::queries::eip::dispatch::DispatchQueryEip;
pub use crate::impls::transaction::convert_gas_to_fee::DynamicConvertCosmosGasToFee;
pub use crate::impls::transaction::convert_gas_to_fee::StaticConvertCosmosGasToFee;
use crate::impls::transaction::encode_tx::EncodeCosmosTx;
use crate::impls::transaction::estimate_fee::EstimateCosmosTxFee;
use crate::impls::transaction::event::ParseCosmosTxResponseAsEvents;
Expand All @@ -28,6 +31,8 @@ use crate::impls::transaction::query_nonce::QueryCosmosAccount;
use crate::impls::transaction::query_tx_response::QueryCosmosTxResponse;
use crate::impls::transaction::submit_tx::BroadcastCosmosTx;
use crate::impls::types::transaction::ProvideCosmosTransactionTypes;
pub use crate::traits::convert_gas_to_fee::GasToFeeConverterComponent;
pub use crate::traits::eip::eip_query::EipQuerierComponent;

define_components! {
CosmosTxComponents {
Expand Down Expand Up @@ -59,6 +64,10 @@ define_components! {
EncodeCosmosTx,
TxFeeEstimatorComponent:
EstimateCosmosTxFee,
GasToFeeConverterComponent:
DynamicConvertCosmosGasToFee,
EipQuerierComponent:
DispatchQueryEip,
TxSubmitterComponent:
BroadcastCosmosTx,
NonceQuerierComponent:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use cgp::core::Async;
use cgp::prelude::HasErrorType;

use crate::impls::queries::eip::feemarket::QueryEipFromFeeMarket;
use crate::impls::queries::eip::osmosis::OsmosisQueryEip;
use crate::traits::eip::eip_query::EipQuerier;
use crate::types::config::gas::dynamic_gas_config::DynamicGasConfig;
use crate::types::config::gas::eip_type::EipQueryType;

pub struct DispatchQueryEip;

impl<Chain> EipQuerier<Chain> for DispatchQueryEip
where
QueryEipFromFeeMarket: EipQuerier<Chain>,
OsmosisQueryEip: EipQuerier<Chain>,
Chain: HasErrorType + Async,
{
async fn query_eip_base_fee(
chain: &Chain,
dynamic_gas_config: &DynamicGasConfig,
) -> Result<f64, Chain::Error> {
match dynamic_gas_config.eip_query_type {
EipQueryType::FeeMarket => {
QueryEipFromFeeMarket::query_eip_base_fee(chain, dynamic_gas_config).await
}
EipQueryType::Osmosis => {
OsmosisQueryEip::query_eip_base_fee(chain, dynamic_gas_config).await
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use cgp::core::error::CanRaiseError;
use core::str::FromStr;
use prost::DecodeError;
use subtle_encoding::base64;

use crate::impls::queries::eip::types::EipBaseFeeHTTPResult;
use crate::impls::queries::eip::types::EipQueryError;
use crate::impls::queries::eip::types::GasPriceResponse;
use crate::traits::eip::eip_query::EipQuerier;
use crate::traits::rpc_client::HasRpcClient;
use crate::types::config::gas::dynamic_gas_config::DynamicGasConfig;

/// Query EIP-1559 base fee using Skip's feemarket endpoint and decode it using
/// `GasPriceResponse`
pub struct QueryEipFromFeeMarket;

impl<Chain> EipQuerier<Chain> for QueryEipFromFeeMarket
where
Chain: HasRpcClient
+ CanRaiseError<reqwest::Error>
+ CanRaiseError<subtle_encoding::Error>
+ CanRaiseError<DecodeError>
+ CanRaiseError<core::num::ParseIntError>
+ CanRaiseError<core::num::ParseFloatError>
+ CanRaiseError<&'static str>
+ CanRaiseError<EipQueryError>,
{
async fn query_eip_base_fee(
chain: &Chain,
dynamic_gas_config: &DynamicGasConfig,
) -> Result<f64, Chain::Error> {
let url = format!(
"{}abci_query?path=\"/feemarket.feemarket.v1.Query/GasPrices\"&denom={}",
chain.rpc_address(),
dynamic_gas_config.denom,
);

let response = reqwest::get(&url).await.map_err(Chain::raise_error)?;

if !response.status().is_success() {
return Err(Chain::raise_error(EipQueryError { response }));
}

let result: EipBaseFeeHTTPResult = response.json().await.map_err(Chain::raise_error)?;

let decoded = base64::decode(result.result.response.value).map_err(Chain::raise_error)?;

let gas_price_response: GasPriceResponse =
prost::Message::decode(decoded.as_ref()).map_err(Chain::raise_error)?;
let dec_coin = gas_price_response
.price
.ok_or_else(|| Chain::raise_error("missing price in GasPriceRespone"))?;

let raw_amount = f64::from_str(&dec_coin.amount).map_err(Chain::raise_error)?;
let amount = raw_amount / 1000000000000000000.0;

Ok(amount)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod dispatch;
pub mod feemarket;
pub mod osmosis;
pub mod types;
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use cgp::core::error::CanRaiseError;
use core::str::FromStr;
use prost::DecodeError;
use subtle_encoding::base64;

use ibc_proto::cosmos::base::v1beta1::DecProto;

use crate::impls::queries::eip::types::EipBaseFeeHTTPResult;
use crate::impls::queries::eip::types::EipQueryError;
use crate::traits::eip::eip_query::EipQuerier;
use crate::traits::rpc_client::HasRpcClient;
use crate::types::config::gas::dynamic_gas_config::DynamicGasConfig;

/// Query EIP-1559 base fee using Osmosis endpoint and decode it using
/// Cosmos SDK proto `DecProto`
pub struct OsmosisQueryEip;

impl<Chain> EipQuerier<Chain> for OsmosisQueryEip
where
Chain: HasRpcClient
+ CanRaiseError<reqwest::Error>
+ CanRaiseError<subtle_encoding::Error>
+ CanRaiseError<DecodeError>
+ CanRaiseError<core::num::ParseIntError>
+ CanRaiseError<core::num::ParseFloatError>
+ CanRaiseError<EipQueryError>,
{
async fn query_eip_base_fee(
chain: &Chain,
_dynamic_gas_config: &DynamicGasConfig,
) -> Result<f64, Chain::Error> {
let url = format!(
"{}abci_query?path=\"/osmosis.txfees.v1beta1.Query/GetEipBaseFee\"",
chain.rpc_address()
);

let response = reqwest::get(&url).await.map_err(Chain::raise_error)?;

if !response.status().is_success() {
return Err(Chain::raise_error(EipQueryError { response }));
}

let result: EipBaseFeeHTTPResult = response.json().await.map_err(Chain::raise_error)?;

let decoded = base64::decode(result.result.response.value).map_err(Chain::raise_error)?;

let dec_proto: DecProto =
prost::Message::decode(decoded.as_ref()).map_err(Chain::raise_error)?;

let raw_amount = f64::from_str(&dec_proto.dec).map_err(Chain::raise_error)?;
let amount = raw_amount / 1000000000000000000.0;

Ok(amount)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use reqwest::Response;
use serde::Deserialize;

use ibc_proto::cosmos::base::v1beta1::DecCoin;

#[derive(Debug)]
pub struct EipQueryError {
pub response: Response,
}

#[derive(Deserialize)]
pub struct EipBaseFeeHTTPResult {
pub result: EipBaseFeeResult,
}

#[derive(Deserialize)]
pub struct EipBaseFeeResult {
pub response: EipBaseFeeResponse,
}

#[derive(Deserialize)]
pub struct EipBaseFeeResponse {
pub value: String,
}

/// GasPriceResponse is the response type for the Query/GasPrice RPC method.
/// Returns a gas price in specified denom.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GasPriceResponse {
#[prost(message, optional, tag = "1")]
pub price: ::core::option::Option<DecCoin>,
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod client_state;
pub mod connection_end;
pub mod consensus_state;
pub mod consensus_state_height;
pub mod eip;
pub mod packet_acknowledgement;
pub mod packet_acknowledgements;
pub mod packet_commitment;
Expand Down
Loading

0 comments on commit cb2d629

Please sign in to comment.