Cosmos gas price config (#3042)

### Description

Adds a cosmos-specific config item for setting the minimum gas price, in
the format returned by the `cosmos.base.node.v1beta1.Service/Config`
cosmos-sdk grpc endpoint.

### Related issues

- Fixes https://github.com/hyperlane-xyz/issues/issues/810

### Backward compatibility

No. Will break existing cosmos configs.


### Testing

None yet, will test in e2e
pull/3084/head
Daniel Savu 11 months ago committed by GitHub
parent 8e44bc1b81
commit d18f7ae8e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .github/ISSUE_TEMPLATE/feature_request.md
  2. 105
      rust/Cargo.lock
  3. 1
      rust/Cargo.toml
  4. 1
      rust/agents/relayer/Cargo.toml
  5. 7
      rust/agents/relayer/src/msg/gas_payment/mod.rs
  6. 6
      rust/agents/relayer/src/msg/gas_payment/policies/minimum.rs
  7. 4
      rust/agents/relayer/src/msg/gas_payment/policies/none.rs
  8. 11
      rust/agents/relayer/src/msg/gas_payment/policies/on_chain_fee_quoting.rs
  9. 2
      rust/agents/relayer/src/msg/pending_message.rs
  10. 2
      rust/chains/hyperlane-cosmos/src/mailbox.rs
  11. 27
      rust/chains/hyperlane-cosmos/src/providers/grpc.rs
  12. 5
      rust/chains/hyperlane-cosmos/src/providers/mod.rs
  13. 44
      rust/chains/hyperlane-cosmos/src/trait_builder.rs
  14. 2
      rust/chains/hyperlane-cosmos/src/types.rs
  15. 9
      rust/chains/hyperlane-ethereum/src/mailbox.rs
  16. 10
      rust/chains/hyperlane-sealevel/src/mailbox.rs
  17. 2
      rust/chains/hyperlane-sealevel/src/validator_announce.rs
  18. 4
      rust/config/mainnet3_config.json
  19. 9
      rust/hyperlane-base/src/settings/parser/connection_parser.rs
  20. 20
      rust/hyperlane-base/src/settings/parser/mod.rs
  21. 1
      rust/hyperlane-core/Cargo.toml
  22. 5
      rust/hyperlane-core/src/error.rs
  23. 10
      rust/hyperlane-core/src/traits/mod.rs
  24. 2
      rust/hyperlane-core/src/types/mod.rs
  25. 93
      rust/hyperlane-core/src/types/primitive_types.rs
  26. 1
      rust/utils/run-locally/Cargo.toml
  27. 5
      rust/utils/run-locally/src/cosmos/cli.rs
  28. 4
      rust/utils/run-locally/src/cosmos/crypto.rs
  29. 3
      rust/utils/run-locally/src/cosmos/mod.rs
  30. 14
      rust/utils/run-locally/src/cosmos/types.rs

@ -4,7 +4,6 @@ about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
## Problem

105
rust/Cargo.lock generated

@ -584,6 +584,19 @@ dependencies = [
"num-traits",
]
[[package]]
name = "bigdecimal"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06619be423ea5bb86c95f087d5707942791a08a85530df0db2209a3ecfb8bc9"
dependencies = [
"autocfg",
"libm",
"num-bigint 0.4.4",
"num-integer",
"num-traits",
]
[[package]]
name = "bincode"
version = "1.3.3"
@ -750,11 +763,11 @@ dependencies = [
[[package]]
name = "borsh"
version = "1.2.1"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9897ef0f1bd2362169de6d7e436ea2237dc1085d7d1e4db75f4be34d86f309d1"
checksum = "26d4d6dafc1a3bb54687538972158f07b2c948bc57d5890df22c0739098b3028"
dependencies = [
"borsh-derive 1.2.1",
"borsh-derive 1.3.0",
"cfg_aliases",
]
@ -773,9 +786,9 @@ dependencies = [
[[package]]
name = "borsh-derive"
version = "1.2.1"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478b41ff04256c5c8330f3dfdaaae2a5cc976a8e75088bafa4625b0d0208de8c"
checksum = "bf4918709cc4dd777ad2b6303ed03cb37f3ca0ccede8c1b0d28ac6db8f4710e0"
dependencies = [
"once_cell",
"proc-macro-crate 2.0.0",
@ -1339,9 +1352,9 @@ checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
[[package]]
name = "const-oid"
version = "0.9.5"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "constant_time_eq"
@ -1954,7 +1967,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
dependencies = [
"const-oid 0.9.5",
"const-oid 0.9.6",
"zeroize",
]
@ -1964,7 +1977,7 @@ version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
dependencies = [
"const-oid 0.9.5",
"const-oid 0.9.6",
"zeroize",
]
@ -2107,7 +2120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer 0.10.4",
"const-oid 0.9.5",
"const-oid 0.9.6",
"crypto-common",
"subtle",
]
@ -3854,11 +3867,11 @@ dependencies = [
[[package]]
name = "home"
version = "0.5.5"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@ -3925,9 +3938,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.27"
version = "0.14.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
dependencies = [
"bytes",
"futures-channel",
@ -3940,7 +3953,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
"socket2 0.4.10",
"socket2 0.5.5",
"tokio",
"tower-service",
"tracing",
@ -4079,6 +4092,7 @@ version = "0.1.0"
dependencies = [
"async-trait",
"auto_impl 1.1.0",
"bigdecimal 0.4.2",
"borsh 0.9.3",
"bs58 0.5.0",
"bytes",
@ -4958,6 +4972,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "libm"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "libredox"
version = "0.0.1"
@ -6698,6 +6718,7 @@ dependencies = [
"itertools 0.11.0",
"num-derive 0.4.1",
"num-traits",
"once_cell",
"prometheus",
"regex",
"reqwest",
@ -6722,9 +6743,9 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.11.22"
version = "0.11.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
dependencies = [
"async-compression",
"base64 0.21.5",
@ -6828,12 +6849,13 @@ dependencies = [
[[package]]
name = "rkyv"
version = "0.7.42"
version = "0.7.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58"
checksum = "527a97cdfef66f65998b5f3b637c26f5a5ec09cc52a3f9932313ac645f4190f5"
dependencies = [
"bitvec 1.0.1",
"bytecheck",
"bytes",
"hashbrown 0.12.3",
"ptr_meta",
"rend",
@ -6845,9 +6867,9 @@ dependencies = [
[[package]]
name = "rkyv_derive"
version = "0.7.42"
version = "0.7.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d"
checksum = "b5c462a1328c8e67e4d6dbad1eb0355dd43e8ab432c6e227a43657f16ade5033"
dependencies = [
"proc-macro2 1.0.70",
"quote 1.0.33",
@ -6917,6 +6939,7 @@ dependencies = [
"hex 0.4.3",
"hpl-interface",
"hyperlane-core",
"hyperlane-cosmos",
"jobserver",
"k256 0.13.2",
"macro_rules_attribute",
@ -7061,7 +7084,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06676aec5ccb8fc1da723cc8c0f9a46549f21ebb8753d3915c6c41db1e7f1dc4"
dependencies = [
"arrayvec",
"borsh 1.2.1",
"borsh 1.3.0",
"bytes",
"num-traits",
"rand 0.8.5",
@ -7419,7 +7442,7 @@ checksum = "fade86e8d41fd1a4721f84cb834f4ca2783f973cc30e6212b7fafc134f169214"
dependencies = [
"async-stream",
"async-trait",
"bigdecimal",
"bigdecimal 0.3.1",
"chrono",
"futures",
"log",
@ -7491,7 +7514,7 @@ version = "0.28.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbab99b8cd878ab7786157b7eb8df96333a6807cc6e45e8888c85b51534b401a"
dependencies = [
"bigdecimal",
"bigdecimal 0.3.1",
"chrono",
"rust_decimal",
"sea-query-derive",
@ -7506,7 +7529,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cea85029985b40dfbf18318d85fe985c04db7c1b4e5e8e0a0a0cdff5f1e30f9"
dependencies = [
"bigdecimal",
"bigdecimal 0.3.1",
"chrono",
"rust_decimal",
"sea-query",
@ -8974,7 +8997,7 @@ dependencies = [
"ahash 0.7.7",
"atoi",
"base64 0.13.1",
"bigdecimal",
"bigdecimal 0.3.1",
"bitflags 1.3.2",
"byteorder",
"bytes",
@ -9477,18 +9500,18 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thiserror"
version = "1.0.50"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
dependencies = [
"proc-macro2 1.0.70",
"quote 1.0.33",
@ -9507,9 +9530,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
dependencies = [
"deranged",
"itoa",
@ -9527,9 +9550,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
dependencies = [
"time-core",
]
@ -10689,9 +10712,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.28"
version = "0.5.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2"
checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5"
dependencies = [
"memchr",
]
@ -10789,18 +10812,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.7.30"
version = "0.7.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7"
checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.30"
version = "0.7.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba"
checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
dependencies = [
"proc-macro2 1.0.70",
"quote 1.0.33",

@ -55,6 +55,7 @@ async-trait = "0.1"
auto_impl = "1.0"
backtrace = "0.3"
base64 = "0.21.2"
bigdecimal = "0.4.2"
bincode = "1.3"
borsh = "0.9"
bs58 = "0.5.0"

@ -39,6 +39,7 @@ hyperlane-base = { path = "../../hyperlane-base" }
hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" }
[dev-dependencies]
once_cell.workspace = true
tokio-test.workspace = true
hyperlane-test = { path = "../../hyperlane-test" }
hyperlane-base = { path = "../../hyperlane-base", features = ["test-utils"] }

@ -4,8 +4,8 @@ use async_trait::async_trait;
use eyre::Result;
use hyperlane_base::db::HyperlaneRocksDB;
use hyperlane_core::{
GasPaymentKey, HyperlaneMessage, InterchainGasExpenditure, InterchainGasPayment,
TxCostEstimate, TxOutcome, U256,
FixedPointNumber, GasPaymentKey, HyperlaneMessage, InterchainGasExpenditure,
InterchainGasPayment, TxCostEstimate, TxOutcome, U256,
};
use tracing::{debug, error, trace};
@ -135,7 +135,8 @@ impl GasPaymentEnforcer {
self.db.process_gas_expenditure(InterchainGasExpenditure {
message_id: message.id(),
gas_used: outcome.gas_used,
tokens_used: outcome.gas_used * outcome.gas_price,
tokens_used: (FixedPointNumber::try_from(outcome.gas_used)? * outcome.gas_price)
.try_into()?,
})?;
Ok(())
}

@ -59,7 +59,7 @@ async fn test_gas_payment_policy_minimum() {
&current_expenditure,
&TxCostEstimate {
gas_limit: U256::from(100000u32),
gas_price: U256::from(100000u32),
gas_price: U256::from(100000u32).try_into().unwrap(),
l2_gas_limit: None,
},
)
@ -83,7 +83,7 @@ async fn test_gas_payment_policy_minimum() {
&current_expenditure,
&TxCostEstimate {
gas_limit: U256::from(100000u32),
gas_price: U256::from(100001u32),
gas_price: U256::from(100001u32).try_into().unwrap(),
l2_gas_limit: None,
},
)
@ -101,7 +101,7 @@ async fn test_gas_payment_policy_minimum() {
&current_expenditure,
&TxCostEstimate {
gas_limit: U256::from(100000u32),
gas_price: U256::from(100001u32),
gas_price: U256::from(100001u32).try_into().unwrap(),
l2_gas_limit: Some(U256::from(22222u32)),
},
)

@ -52,7 +52,7 @@ async fn test_gas_payment_policy_none() {
&current_expenditure,
&TxCostEstimate {
gas_limit: U256::from(100000u32),
gas_price: U256::from(100001u32),
gas_price: U256::from(100001u32).try_into().unwrap(),
l2_gas_limit: None,
},
)
@ -70,7 +70,7 @@ async fn test_gas_payment_policy_none() {
&current_expenditure,
&TxCostEstimate {
gas_limit: U256::from(100000u32),
gas_price: U256::from(100001u32),
gas_price: U256::from(100001u32).try_into().unwrap(),
l2_gas_limit: Some(U256::from(22222u32)),
},
)

@ -64,6 +64,7 @@ impl GasPaymentPolicy for GasPaymentPolicyOnChainFeeQuoting {
#[cfg(test)]
mod test {
use hyperlane_core::H256;
use once_cell::sync::Lazy;
use super::*;
@ -85,11 +86,11 @@ mod test {
}
const MIN: U256 = U256([1000, 0, 0, 0]);
const COST_ESTIMATE: TxCostEstimate = TxCostEstimate {
static COST_ESTIMATE: Lazy<TxCostEstimate> = Lazy::new(|| TxCostEstimate {
gas_limit: U256([2000, 0, 0, 0]), // MIN * 2
gas_price: U256([100001, 0, 0, 0]),
gas_price: U256([100001, 0, 0, 0]).try_into().unwrap(),
l2_gas_limit: None,
};
});
#[test]
fn ensure_little_endian() {
@ -203,7 +204,7 @@ mod test {
let tx_cost_estimate = TxCostEstimate {
gas_limit: MIN * 100, // Large gas limit
gas_price: COST_ESTIMATE.gas_price,
gas_price: COST_ESTIMATE.gas_price.clone().try_into().unwrap(),
l2_gas_limit: Some(MIN * 2),
};
@ -217,7 +218,7 @@ mod test {
&current_expenditure(0),
&TxCostEstimate {
l2_gas_limit: None,
..tx_cost_estimate
..tx_cost_estimate.clone()
}
)
.await

@ -238,7 +238,7 @@ impl PendingOperation for PendingMessage {
"processing message"
);
op_try!(critical: self.ctx.origin_gas_payment_enforcer.record_tx_outcome(&self.message, tx_outcome), "recording tx outcome");
op_try!(critical: self.ctx.origin_gas_payment_enforcer.record_tx_outcome(&self.message, tx_outcome.clone()), "recording tx outcome");
if tx_outcome.executed {
info!(
txid=?tx_outcome.transaction_id,

@ -215,7 +215,7 @@ impl Mailbox for CosmosMailbox {
let result = TxCostEstimate {
gas_limit: gas_limit.into(),
gas_price: U256::from(2500),
gas_price: self.provider.grpc().gas_price(),
l2_gas_limit: None,
};

@ -22,19 +22,18 @@ use cosmrs::{
traits::Message,
},
tx::{self, Fee, MessageExt, SignDoc, SignerInfo},
Amount, Coin,
Coin,
};
use hyperlane_core::{
ChainCommunicationError, ChainResult, ContractLocator, FixedPointNumber, U256,
};
use hyperlane_core::{ChainCommunicationError, ChainResult, ContractLocator, U256};
use serde::Serialize;
use tonic::transport::{Channel, Endpoint};
use crate::address::CosmosAddress;
use crate::HyperlaneCosmosError;
use crate::{address::CosmosAddress, CosmosAmount};
use crate::{signers::Signer, ConnectionConf};
/// The gas price to use for transactions.
/// TODO: is there a nice way to get a suggested price dynamically?
const DEFAULT_GAS_PRICE: f64 = 0.05;
/// A multiplier applied to a simulated transaction's gas usage to
/// calculate the estimated gas.
const GAS_ESTIMATE_MULTIPLIER: f64 = 1.25;
@ -91,12 +90,14 @@ pub struct WasmGrpcProvider {
/// GRPC Channel that can be cheaply cloned.
/// See `<https://docs.rs/tonic/latest/tonic/transport/struct.Channel.html#multiplexing-requests>`
channel: Channel,
gas_price: CosmosAmount,
}
impl WasmGrpcProvider {
/// Create new CosmWasm GRPC Provider.
pub fn new(
conf: ConnectionConf,
gas_price: CosmosAmount,
locator: Option<ContractLocator>,
signer: Option<Signer>,
) -> ChainResult<Self> {
@ -112,6 +113,7 @@ impl WasmGrpcProvider {
contract_address,
signer,
channel,
gas_price,
})
}
@ -121,9 +123,12 @@ impl WasmGrpcProvider {
.as_ref()
.ok_or(ChainCommunicationError::SignerUnavailable)
}
}
impl WasmGrpcProvider {
/// Get the gas price
pub fn gas_price(&self) -> FixedPointNumber {
self.gas_price.amount.clone()
}
/// Generates an unsigned SignDoc for a transaction.
async fn generate_unsigned_sign_doc(
&self,
@ -145,9 +150,13 @@ impl WasmGrpcProvider {
);
let signer_info = SignerInfo::single_direct(Some(signer.public_key), account_info.sequence);
let amount: u128 = (FixedPointNumber::from(gas_limit) * self.gas_price())
.ceil_to_integer()
.try_into()?;
let auth_info = signer_info.auth_info(Fee::from_amount_and_gas(
Coin::new(
Amount::from((gas_limit as f64 * DEFAULT_GAS_PRICE) as u64),
// The fee to pay is the gas limit * the gas price
amount,
self.conf.get_canonical_asset().as_str(),
)
.map_err(Into::<HyperlaneCosmosError>::into)?,

@ -5,7 +5,7 @@ use hyperlane_core::{
};
use tendermint_rpc::{client::CompatMode, HttpClient};
use crate::{ConnectionConf, HyperlaneCosmosError, Signer};
use crate::{ConnectionConf, CosmosAmount, HyperlaneCosmosError, Signer};
use self::grpc::WasmGrpcProvider;
@ -31,7 +31,8 @@ impl CosmosProvider {
locator: Option<ContractLocator>,
signer: Option<Signer>,
) -> ChainResult<Self> {
let grpc_client = WasmGrpcProvider::new(conf.clone(), locator, signer)?;
let gas_price = CosmosAmount::try_from(conf.get_minimum_gas_price().clone())?;
let grpc_client = WasmGrpcProvider::new(conf.clone(), gas_price.clone(), locator, signer)?;
let rpc_client = HttpClient::builder(
conf.get_rpc_url()
.parse()

@ -1,3 +1,8 @@
use std::str::FromStr;
use derive_new::new;
use hyperlane_core::{ChainCommunicationError, FixedPointNumber};
/// Cosmos connection configuration
#[derive(Debug, Clone)]
pub struct ConnectionConf {
@ -11,6 +16,38 @@ pub struct ConnectionConf {
prefix: String,
/// Canoncial Assets Denom
canonical_asset: String,
/// The gas price set by the cosmos-sdk validator. Note that this represents the
/// minimum price set by the validator.
/// More details here: https://docs.cosmos.network/main/learn/beginner/gas-fees#antehandler
gas_price: RawCosmosAmount,
}
/// Untyped cosmos amount
#[derive(serde::Serialize, serde::Deserialize, new, Clone, Debug)]
pub struct RawCosmosAmount {
/// Coin denom (e.g. `untrn`)
pub denom: String,
/// Amount in the given denom
pub amount: String,
}
/// Typed cosmos amount
#[derive(Clone, Debug)]
pub struct CosmosAmount {
/// Coin denom (e.g. `untrn`)
pub denom: String,
/// Amount in the given denom
pub amount: FixedPointNumber,
}
impl TryFrom<RawCosmosAmount> for CosmosAmount {
type Error = ChainCommunicationError;
fn try_from(raw: RawCosmosAmount) -> Result<Self, ChainCommunicationError> {
Ok(Self {
denom: raw.denom,
amount: FixedPointNumber::from_str(&raw.amount)?,
})
}
}
/// An error type when parsing a connection configuration.
@ -59,6 +96,11 @@ impl ConnectionConf {
self.canonical_asset.clone()
}
/// Get the minimum gas price
pub fn get_minimum_gas_price(&self) -> RawCosmosAmount {
self.gas_price.clone()
}
/// Create a new connection configuration
pub fn new(
grpc_url: String,
@ -66,6 +108,7 @@ impl ConnectionConf {
chain_id: String,
prefix: String,
canonical_asset: String,
minimum_gas_price: RawCosmosAmount,
) -> Self {
Self {
grpc_url,
@ -73,6 +116,7 @@ impl ConnectionConf {
chain_id,
prefix,
canonical_asset,
gas_price: minimum_gas_price,
}
}
}

@ -29,6 +29,6 @@ pub fn tx_response_to_outcome(response: TxResponse) -> ChainResult<TxOutcome> {
transaction_id: H256::from_slice(hex::decode(response.txhash)?.as_slice()).into(),
executed: response.code == 0,
gas_used: U256::from(response.gas_used),
gas_price: U256::one(),
gas_price: U256::one().try_into()?,
})
}

@ -373,15 +373,16 @@ where
None
};
let gas_price = self
let gas_price: U256 = self
.provider
.get_gas_price()
.await
.map_err(ChainCommunicationError::from_other)?;
.map_err(ChainCommunicationError::from_other)?
.into();
Ok(TxCostEstimate {
gas_limit: gas_limit.into(),
gas_price: gas_price.into(),
gas_price: gas_price.try_into()?,
l2_gas_limit: l2_gas_limit.map(|v| v.into()),
})
}
@ -484,7 +485,7 @@ mod test {
tx_cost_estimate,
TxCostEstimate {
gas_limit: estimated_gas_limit,
gas_price,
gas_price: gas_price.try_into().unwrap(),
l2_gas_limit: Some(l2_gas_limit),
},
);

@ -9,9 +9,9 @@ use tracing::{debug, info, instrument, warn};
use hyperlane_core::{
accumulator::incremental::IncrementalMerkle, ChainCommunicationError, ChainResult, Checkpoint,
ContractLocator, Decode as _, Encode as _, HyperlaneAbi, HyperlaneChain, HyperlaneContract,
HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, Indexer, LogMeta, Mailbox,
MerkleTreeHook, SequenceIndexer, TxCostEstimate, TxOutcome, H256, H512, U256,
ContractLocator, Decode as _, Encode as _, FixedPointNumber, HyperlaneAbi, HyperlaneChain,
HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, Indexer, LogMeta,
Mailbox, MerkleTreeHook, SequenceIndexer, TxCostEstimate, TxOutcome, H256, H512, U256,
};
use hyperlane_sealevel_interchain_security_module_interface::{
InterchainSecurityModuleInstruction, VerifyInstruction,
@ -470,7 +470,7 @@ impl Mailbox for SealevelMailbox {
transaction_id: txid,
executed,
// TODO use correct data upon integrating IGP support
gas_price: U256::zero(),
gas_price: U256::zero().try_into()?,
gas_used: U256::zero(),
})
}
@ -484,7 +484,7 @@ impl Mailbox for SealevelMailbox {
// TODO use correct data upon integrating IGP support
Ok(TxCostEstimate {
gas_limit: U256::zero(),
gas_price: U256::zero(),
gas_price: FixedPointNumber::zero(),
l2_gas_limit: None,
})
}

@ -128,7 +128,7 @@ impl ValidatorAnnounce for SealevelValidatorAnnounce {
transaction_id: H512::zero(),
executed: false,
gas_used: U256::zero(),
gas_price: U256::zero(),
gas_price: U256::zero().try_into()?,
})
}
}

@ -432,6 +432,10 @@
"grpcUrl": "https://grpc-kralum.neutron-1.neutron.org:80",
"canonicalAsset": "untrn",
"prefix": "neutron",
"gasPrice": {
"amount": "0.5",
"denom": "untrn"
},
"index": {
"from": 4000000,
"chunk": 100000

@ -6,7 +6,7 @@ use url::Url;
use crate::settings::envs::*;
use crate::settings::ChainConnectionConf;
use super::ValueParser;
use super::{parse_cosmos_gas_price, ValueParser};
pub fn build_ethereum_connection_conf(
rpcs: &[Url],
@ -94,6 +94,12 @@ pub fn build_cosmos_connection_conf(
None
};
let gas_price = chain
.chain(err)
.get_opt_key("gasPrice")
.and_then(parse_cosmos_gas_price)
.end();
if !local_err.is_ok() {
err.merge(local_err);
None
@ -104,6 +110,7 @@ pub fn build_cosmos_connection_conf(
chain_id.unwrap().to_string(),
prefix.unwrap().to_string(),
canonical_asset.unwrap(),
gas_price.unwrap(),
)))
}
}

@ -11,6 +11,7 @@ use std::{
use convert_case::{Case, Casing};
use eyre::{eyre, Context};
use h_cosmos::RawCosmosAmount;
use hyperlane_core::{
cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol, IndexMode,
};
@ -398,3 +399,22 @@ pub fn recase_json_value(mut val: Value, case: Case) -> Value {
}
val
}
/// Expects AgentSigner.
fn parse_cosmos_gas_price(gas_price: ValueParser) -> ConfigResult<RawCosmosAmount> {
let mut err = ConfigParsingError::default();
let amount = gas_price
.chain(&mut err)
.get_opt_key("amount")
.parse_string()
.end();
let denom = gas_price
.chain(&mut err)
.get_opt_key("denom")
.parse_string()
.end();
cfg_unwrap_all!(&gas_price.cwp, err: [denom, amount]);
err.into_result(RawCosmosAmount::new(denom.to_owned(), amount.to_owned()))
}

@ -12,6 +12,7 @@ version = { workspace = true }
[dependencies]
async-trait.workspace = true
auto_impl.workspace = true
bigdecimal.workspace = true
borsh.workspace = true
bs58.workspace = true
bytes = { workspace = true, features = ["serde"] }

@ -3,6 +3,8 @@ use std::error::Error as StdError;
use std::fmt::{Debug, Display, Formatter};
use std::ops::Deref;
use bigdecimal::ParseBigDecimalError;
use crate::config::StrOrIntParseError;
use std::string::FromUtf8Error;
@ -123,6 +125,9 @@ pub enum ChainCommunicationError {
/// Primitive type error
#[error(transparent)]
PrimitiveTypeError(#[from] PrimitiveTypeError),
/// Big decimal parsing error
#[error(transparent)]
ParseBigDecimalError(#[from] ParseBigDecimalError),
}
impl ChainCommunicationError {

@ -15,6 +15,8 @@ pub use routing_ism::*;
pub use signing::*;
pub use validator_announce::*;
use crate::{FixedPointNumber, U256};
mod aggregation_ism;
mod ccip_read_ism;
mod cursor;
@ -33,7 +35,7 @@ mod signing;
mod validator_announce;
/// The result of a transaction
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub struct TxOutcome {
/// The transaction identifier/hash
pub transaction_id: crate::H512,
@ -42,7 +44,7 @@ pub struct TxOutcome {
/// Amount of gas used on this transaction.
pub gas_used: crate::U256,
/// Price paid for the gas
pub gas_price: crate::U256,
pub gas_price: FixedPointNumber,
// TODO: more? What can be abstracted across all chains?
}
@ -55,8 +57,8 @@ impl From<ethers_core::types::TransactionReceipt> for TxOutcome {
gas_used: t.gas_used.map(Into::into).unwrap_or(crate::U256::zero()),
gas_price: t
.effective_gas_price
.map(Into::into)
.unwrap_or(crate::U256::zero()),
.and_then(|price| U256::from(price).try_into().ok())
.unwrap_or(FixedPointNumber::zero()),
}
}
}

@ -224,7 +224,7 @@ pub struct TxCostEstimate {
/// The gas limit for the transaction.
pub gas_limit: U256,
/// The gas price for the transaction.
pub gas_price: U256,
pub gas_price: FixedPointNumber,
/// The amount of L2 gas for the transaction.
/// If Some, `gas_limit` is the sum of the gas limit
/// covering L1 costs and the L2 gas limit.

@ -3,11 +3,15 @@
#![allow(clippy::assign_op_pattern)]
#![allow(clippy::reversed_empty_ranges)]
use std::{ops::Mul, str::FromStr};
use bigdecimal::BigDecimal;
use borsh::{BorshDeserialize, BorshSerialize};
use fixed_hash::impl_fixed_hash_conversions;
use num_traits::Zero;
use uint::construct_uint;
use crate::types::serialize;
use crate::{types::serialize, ChainCommunicationError};
/// Error type for conversion.
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
@ -337,3 +341,90 @@ impl From<solana_sdk::signature::Signature> for H512 {
H512(sig.into())
}
}
/// Wrapper type around `BigDecimal` to implement various traits on it
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FixedPointNumber(BigDecimal);
impl FixedPointNumber {
/// Zero
pub fn zero() -> Self {
Self(BigDecimal::zero())
}
/// Round up to the nearest integer
pub fn ceil_to_integer(&self) -> Self {
Self(self.0.with_scale(0))
}
/// Ceil
pub fn ceil(&self, fractional_digit_count: i64) -> Self {
Self(
self.0
.with_scale_round(fractional_digit_count, bigdecimal::RoundingMode::Ceiling),
)
}
}
impl Default for FixedPointNumber {
fn default() -> Self {
Self::zero()
}
}
impl TryFrom<U256> for FixedPointNumber {
type Error = ChainCommunicationError;
fn try_from(val: U256) -> Result<Self, Self::Error> {
let u256_string = val.to_string();
Ok(Self(BigDecimal::from_str(&u256_string)?))
}
}
impl TryInto<U256> for FixedPointNumber {
type Error = ChainCommunicationError;
fn try_into(self) -> Result<U256, Self::Error> {
// Remove all decimals
let big_integer_string = self.0.with_scale(0).to_string();
let value = U256::from_dec_str(&big_integer_string)?;
Ok(value)
}
}
impl TryInto<u128> for FixedPointNumber {
type Error = ChainCommunicationError;
fn try_into(self) -> Result<u128, Self::Error> {
let u256: U256 = self.try_into()?;
Ok(u256.as_u128())
}
}
impl<T> From<T> for FixedPointNumber
where
T: Into<BigDecimal>,
{
fn from(val: T) -> Self {
Self(val.into())
}
}
impl<T> Mul<T> for FixedPointNumber
where
T: Into<FixedPointNumber>,
{
type Output = FixedPointNumber;
fn mul(self, rhs: T) -> Self::Output {
let rhs = rhs.into();
Self(self.0 * rhs.0)
}
}
impl FromStr for FixedPointNumber {
type Err = ChainCommunicationError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(BigDecimal::from_str(s)?))
}
}

@ -11,6 +11,7 @@ version.workspace = true
[dependencies]
hyperlane-core = { path = "../../hyperlane-core", features = ["float"]}
hyperlane-cosmos = { path = "../../chains/hyperlane-cosmos"}
toml_edit.workspace = true
k256.workspace = true
jobserver.workspace = true

@ -1,5 +1,6 @@
use std::{collections::BTreeMap, io::Write, path::PathBuf, process::Stdio};
use hyperlane_cosmos::RawCosmosAmount;
use k256::ecdsa::SigningKey;
use crate::{
@ -9,7 +10,7 @@ use crate::{
use super::{
crypto::KeyPair, default_keys, modify_toml, sed, types::BalanceResponse, wait_for_node, Codes,
Coin, TxResponse,
TxResponse,
};
const GENESIS_FUND: u128 = 1000000000000;
@ -253,7 +254,7 @@ impl OsmosisCLI {
sender: &str,
contract: &str,
execute_msg: T,
funds: Vec<Coin>,
funds: Vec<RawCosmosAmount>,
) -> TxResponse {
let mut cmd = self
.cli()

@ -1,6 +1,4 @@
// TODO: this file can be removed if `CosmosAddress` can be imported from `hyperlane-cosmos`.
// However, adding a hyperlane-cosmos dep creates a dep cycle.
// Look into how this can be fixed.
// TODO: this file can be removed by replacing `KeyPair` uses with `CosmosAddress`
use k256::ecdsa::{SigningKey, VerifyingKey};
use ripemd::Ripemd160;

@ -6,6 +6,7 @@ use std::{env, fs};
use cosmwasm_schema::cw_serde;
use hpl_interface::types::bech32_decode;
use hyperlane_cosmos::RawCosmosAmount;
use macro_rules_attribute::apply;
use maplit::hashmap;
use tempfile::tempdir;
@ -506,7 +507,7 @@ fn run_locally() {
metadata: "".to_string(),
},
},
vec![Coin {
vec![RawCosmosAmount {
denom: "uosmo".to_string(),
amount: 25_000_000.to_string(),
}],

@ -1,6 +1,7 @@
use std::{collections::BTreeMap, path::PathBuf};
use hpl_interface::types::bech32_decode;
use hyperlane_cosmos::RawCosmosAmount;
use super::{cli::OsmosisCLI, CosmosNetwork};
@ -35,12 +36,6 @@ pub struct TxResponse {
pub logs: Vec<TxLog>,
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Coin {
pub denom: String,
pub amount: String,
}
#[derive(serde::Serialize, serde::Deserialize, Clone)]
pub struct Codes {
pub hpl_hook_merkle: u64,
@ -73,7 +68,7 @@ pub struct Deployments {
#[derive(serde::Serialize, serde::Deserialize)]
pub struct BalanceResponse {
pub balances: Vec<Coin>,
pub balances: Vec<RawCosmosAmount>,
}
#[derive(serde::Serialize, serde::Deserialize)]
@ -125,6 +120,7 @@ pub struct AgentConfig {
pub prefix: String,
pub signer: AgentConfigSigner,
pub index: AgentConfigIndex,
pub gas_price: RawCosmosAmount,
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
@ -164,6 +160,10 @@ impl AgentConfig {
key: format!("0x{}", hex::encode(validator.priv_key.to_bytes())),
prefix: "osmo".to_string(),
},
gas_price: RawCosmosAmount {
denom: "uosmo".to_string(),
amount: "0.05".to_string(),
},
index: AgentConfigIndex {
from: 1,
chunk: 100,

Loading…
Cancel
Save