diff --git a/.changeset/healthy-boats-lie.md b/.changeset/healthy-boats-lie.md new file mode 100644 index 0000000000..2eb1bb0a46 --- /dev/null +++ b/.changeset/healthy-boats-lie.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/utils': patch +--- + +fix median utils func + add test diff --git a/.changeset/real-starfishes-fold.md b/.changeset/real-starfishes-fold.md deleted file mode 100644 index f161beae68..0000000000 --- a/.changeset/real-starfishes-fold.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/core': patch ---- - -Added overrides for transferFrom, totalSupply to reflect the internal share based accounting for the 4626 mirror asset diff --git a/rust/main/Cargo.lock b/rust/main/Cargo.lock index 87d2c813e9..27b11dcab1 100644 --- a/rust/main/Cargo.lock +++ b/rust/main/Cargo.lock @@ -7670,7 +7670,6 @@ dependencies = [ "ethers", "eyre", "futures", - "hex 0.1.0", "hyperlane-base", "hyperlane-core", "hyperlane-test", diff --git a/rust/main/agents/scraper/Cargo.toml b/rust/main/agents/scraper/Cargo.toml index 7bad1ec7eb..0b9e429b9b 100644 --- a/rust/main/agents/scraper/Cargo.toml +++ b/rust/main/agents/scraper/Cargo.toml @@ -29,7 +29,6 @@ tokio = { workspace = true, features = ["rt", "macros", "parking_lot"] } tracing-futures.workspace = true tracing.workspace = true -hex = { path = "../../utils/hex" } hyperlane-base = { path = "../../hyperlane-base" } hyperlane-core = { path = "../../hyperlane-core", features = ["agent"] } migration = { path = "migration" } diff --git a/rust/main/agents/scraper/src/chain_scraper/mod.rs b/rust/main/agents/scraper/src/chain_scraper/mod.rs index c23df70a16..0be5b2ef13 100644 --- a/rust/main/agents/scraper/src/chain_scraper/mod.rs +++ b/rust/main/agents/scraper/src/chain_scraper/mod.rs @@ -7,14 +7,15 @@ use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; use eyre::Result; +use itertools::Itertools; +use tracing::{trace, warn}; + use hyperlane_base::settings::IndexSettings; use hyperlane_core::{ unwrap_or_none_result, BlockId, BlockInfo, Delivery, HyperlaneDomain, HyperlaneLogStore, HyperlaneMessage, HyperlaneProvider, HyperlaneSequenceAwareIndexerStoreReader, - HyperlaneWatermarkedLogStore, Indexed, InterchainGasPayment, LogMeta, H256, + HyperlaneWatermarkedLogStore, Indexed, InterchainGasPayment, LogMeta, H256, H512, }; -use itertools::Itertools; -use tracing::{trace, warn}; use crate::db::{ BasicBlock, BlockCursor, ScraperDb, StorableDelivery, StorableMessage, StorablePayment, @@ -78,12 +79,10 @@ impl HyperlaneSqlDb { &self, log_meta: impl Iterator, ) -> Result> { - let block_id_by_txn_hash: HashMap = log_meta + let block_id_by_txn_hash: HashMap = log_meta .map(|meta| { ( - meta.transaction_id - .try_into() - .expect("256-bit transaction ids are the maximum supported at this time"), + meta.transaction_id, BlockId::new(meta.block_hash, meta.block_number), ) }) @@ -121,7 +120,7 @@ impl HyperlaneSqlDb { txns: impl Iterator, ) -> Result> { // mapping of txn hash to (txn_id, block_id). - let mut txns: HashMap, i64)> = txns + let mut txns: HashMap, i64)> = txns .map(|TxnWithBlockId { txn_hash, block_id }| (txn_hash, (None, block_id))) .collect(); @@ -145,9 +144,9 @@ impl HyperlaneSqlDb { let mut txns_to_fetch = txns.iter_mut().filter(|(_, id)| id.0.is_none()); let mut txns_to_insert: Vec = Vec::with_capacity(CHUNK_SIZE); - let mut hashes_to_insert: Vec<&H256> = Vec::with_capacity(CHUNK_SIZE); + let mut hashes_to_insert: Vec<&H512> = Vec::with_capacity(CHUNK_SIZE); - for mut chunk in as_chunks::<(&H256, &mut (Option, i64))>(txns_to_fetch, CHUNK_SIZE) { + for mut chunk in as_chunks::<(&H512, &mut (Option, i64))>(txns_to_fetch, CHUNK_SIZE) { for (hash, (_, block_id)) in chunk.iter() { let info = match self.provider.get_txn_by_hash(hash).await { Ok(info) => info, @@ -302,7 +301,7 @@ impl HyperlaneLogStore for HyperlaneSqlDb { if messages.is_empty() { return Ok(0); } - let txns: HashMap = self + let txns: HashMap = self .ensure_blocks_and_txns(messages.iter().map(|r| &r.1)) .await? .map(|t| (t.hash, t)) @@ -310,13 +309,8 @@ impl HyperlaneLogStore for HyperlaneSqlDb { let storable = messages .iter() .filter_map(|(message, meta)| { - txns.get( - &meta - .transaction_id - .try_into() - .expect("256-bit transaction ids are the maximum supported at this time"), - ) - .map(|t| (message.inner().clone(), meta, t.id)) + txns.get(&meta.transaction_id) + .map(|t| (message.inner().clone(), meta, t.id)) }) .map(|(msg, meta, txn_id)| StorableMessage { msg, meta, txn_id }); let stored = self @@ -336,7 +330,7 @@ impl HyperlaneLogStore for HyperlaneSqlDb { if deliveries.is_empty() { return Ok(0); } - let txns: HashMap = self + let txns: HashMap = self .ensure_blocks_and_txns(deliveries.iter().map(|r| &r.1)) .await? .map(|t| (t.hash, t)) @@ -344,13 +338,8 @@ impl HyperlaneLogStore for HyperlaneSqlDb { let storable = deliveries .iter() .filter_map(|(message_id, meta)| { - txns.get( - &meta - .transaction_id - .try_into() - .expect("256-bit transaction ids are the maximum supported at this time"), - ) - .map(|txn| (*message_id.inner(), meta, txn.id)) + txns.get(&meta.transaction_id) + .map(|txn| (*message_id.inner(), meta, txn.id)) }) .map(|(message_id, meta, txn_id)| StorableDelivery { message_id, @@ -378,7 +367,7 @@ impl HyperlaneLogStore for HyperlaneSqlDb { if payments.is_empty() { return Ok(0); } - let txns: HashMap = self + let txns: HashMap = self .ensure_blocks_and_txns(payments.iter().map(|r| &r.1)) .await? .map(|t| (t.hash, t)) @@ -386,13 +375,8 @@ impl HyperlaneLogStore for HyperlaneSqlDb { let storable = payments .iter() .filter_map(|(payment, meta)| { - txns.get( - &meta - .transaction_id - .try_into() - .expect("256-bit transaction ids are the maximum supported at this time"), - ) - .map(|txn| (payment.inner(), meta, txn.id)) + txns.get(&meta.transaction_id) + .map(|txn| (payment.inner(), meta, txn.id)) }) .map(|(payment, meta, txn_id)| StorablePayment { payment, @@ -446,13 +430,13 @@ where #[derive(Debug, Clone)] struct TxnWithId { - hash: H256, + hash: H512, id: i64, } #[derive(Debug, Clone)] struct TxnWithBlockId { - txn_hash: H256, + txn_hash: H512, block_id: i64, } diff --git a/rust/main/agents/scraper/src/conversions.rs b/rust/main/agents/scraper/src/conversions.rs index 84a64a8577..9252d5cd44 100644 --- a/rust/main/agents/scraper/src/conversions.rs +++ b/rust/main/agents/scraper/src/conversions.rs @@ -1,36 +1,7 @@ use num_bigint::{BigInt, Sign}; use sea_orm::prelude::BigDecimal; -use hyperlane_core::{H256, U256}; - -// Creates a big-endian hex representation of the address -pub fn address_to_bytes(data: &H256) -> Vec { - if hex::is_h160(data.as_fixed_bytes()) { - // take the last 20 bytes - data.as_fixed_bytes()[12..32].into() - } else { - h256_to_bytes(data) - } -} - -// Creates a big-endian hex representation of the address -pub fn bytes_to_address(data: Vec) -> eyre::Result { - if (data.len() != 20) && (data.len() != 32) { - return Err(eyre::eyre!("Invalid address length")); - } - if data.len() == 20 { - let mut prefix = vec![0; 12]; - prefix.extend(data); - Ok(H256::from_slice(&prefix[..])) - } else { - Ok(H256::from_slice(&data[..])) - } -} - -// Creates a big-endian hex representation of the address hash -pub fn h256_to_bytes(data: &H256) -> Vec { - data.as_fixed_bytes().as_slice().into() -} +use hyperlane_core::U256; pub fn u256_to_decimal(v: U256) -> BigDecimal { let mut buf = [0u8; 32]; diff --git a/rust/main/agents/scraper/src/db/block.rs b/rust/main/agents/scraper/src/db/block.rs index 1af05cd8c4..45ff0bf7c8 100644 --- a/rust/main/agents/scraper/src/db/block.rs +++ b/rust/main/agents/scraper/src/db/block.rs @@ -5,10 +5,9 @@ use sea_orm::{ }; use tracing::{debug, trace}; -use hyperlane_core::{BlockInfo, H256}; +use hyperlane_core::{address_to_bytes, h256_to_bytes, BlockInfo, H256}; use migration::OnConflict; -use crate::conversions::{address_to_bytes, h256_to_bytes}; use crate::date_time; use crate::db::ScraperDb; diff --git a/rust/main/agents/scraper/src/db/message.rs b/rust/main/agents/scraper/src/db/message.rs index f8b99fec50..423c24058a 100644 --- a/rust/main/agents/scraper/src/db/message.rs +++ b/rust/main/agents/scraper/src/db/message.rs @@ -5,10 +5,11 @@ use itertools::Itertools; use sea_orm::{prelude::*, ActiveValue::*, DeriveColumn, EnumIter, Insert, QuerySelect}; use tracing::{debug, instrument, trace}; -use hyperlane_core::{HyperlaneMessage, LogMeta, H256}; +use hyperlane_core::{ + address_to_bytes, bytes_to_address, h256_to_bytes, HyperlaneMessage, LogMeta, H256, +}; use migration::OnConflict; -use crate::conversions::{address_to_bytes, bytes_to_address, h256_to_bytes}; use crate::date_time; use crate::db::ScraperDb; diff --git a/rust/main/agents/scraper/src/db/payment.rs b/rust/main/agents/scraper/src/db/payment.rs index 250f4f3c6f..498128c34b 100644 --- a/rust/main/agents/scraper/src/db/payment.rs +++ b/rust/main/agents/scraper/src/db/payment.rs @@ -3,10 +3,10 @@ use itertools::Itertools; use sea_orm::{prelude::*, ActiveValue::*, Insert, QuerySelect}; use tracing::{debug, instrument, trace}; -use hyperlane_core::{InterchainGasPayment, LogMeta}; +use hyperlane_core::{h256_to_bytes, InterchainGasPayment, LogMeta}; use migration::OnConflict; -use crate::conversions::{h256_to_bytes, u256_to_decimal}; +use crate::conversions::u256_to_decimal; use crate::date_time; use crate::db::ScraperDb; diff --git a/rust/main/agents/scraper/src/db/txn.rs b/rust/main/agents/scraper/src/db/txn.rs index ff0e70f2b5..d5cec03dc7 100644 --- a/rust/main/agents/scraper/src/db/txn.rs +++ b/rust/main/agents/scraper/src/db/txn.rs @@ -2,19 +2,17 @@ use std::collections::HashMap; use derive_more::Deref; use eyre::{eyre, Context, Result}; -use hyperlane_core::{TxnInfo, H256}; use sea_orm::{ prelude::*, sea_query::OnConflict, ActiveValue::*, DeriveColumn, EnumIter, Insert, NotSet, QuerySelect, }; use tracing::{debug, instrument, trace}; +use hyperlane_core::{address_to_bytes, bytes_to_h512, h512_to_bytes, TxnInfo, H512}; + use super::generated::transaction; -use crate::{ - conversions::{address_to_bytes, h256_to_bytes, u256_to_decimal}, - date_time, - db::ScraperDb, -}; + +use crate::{conversions::u256_to_decimal, date_time, db::ScraperDb}; #[derive(Debug, Clone, Deref)] pub struct StorableTxn { @@ -43,8 +41,8 @@ impl ScraperDb { /// found be excluded from the hashmap. pub async fn get_txn_ids( &self, - hashes: impl Iterator, - ) -> Result> { + hashes: impl Iterator, + ) -> Result> { #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] enum QueryAs { Id, @@ -53,7 +51,7 @@ impl ScraperDb { // check database to see which txns we already know and fetch their IDs let txns = transaction::Entity::find() - .filter(transaction::Column::Hash.is_in(hashes.map(h256_to_bytes))) + .filter(transaction::Column::Hash.is_in(hashes.map(h512_to_bytes))) .select_only() .column_as(transaction::Column::Id, QueryAs::Id) .column_as(transaction::Column::Hash, QueryAs::Hash) @@ -62,7 +60,7 @@ impl ScraperDb { .await .context("When querying transactions")? .into_iter() - .map(|(id, hash)| Ok((H256::from_slice(&hash), id))) + .map(|(id, hash)| Ok((bytes_to_h512(&hash), id))) .collect::>>()?; trace!(?txns, "Queried transaction info for hashes"); @@ -86,7 +84,7 @@ impl ScraperDb { max_priority_fee_per_gas: Set(txn .max_priority_fee_per_gas .map(u256_to_decimal)), - hash: Unchanged(h256_to_bytes(&txn.hash)), + hash: Unchanged(h512_to_bytes(&txn.hash)), time_created: Set(date_time::now()), gas_used: Set(u256_to_decimal(receipt.gas_used)), gas_price: Set(txn.gas_price.map(u256_to_decimal)), diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider.rs b/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider.rs index ef0c153458..e718df6102 100644 --- a/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider.rs +++ b/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider.rs @@ -17,9 +17,9 @@ use tracing::{error, warn}; use crypto::decompress_public_key; use hyperlane_core::{ - AccountAddressType, BlockInfo, ChainCommunicationError, ChainInfo, ChainResult, - ContractLocator, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, HyperlaneProviderError, - TxnInfo, TxnReceiptInfo, H256, U256, + bytes_to_h512, h512_to_bytes, AccountAddressType, BlockInfo, ChainCommunicationError, + ChainInfo, ChainResult, ContractLocator, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, + HyperlaneProviderError, TxnInfo, TxnReceiptInfo, H256, H512, U256, }; use crate::grpc::{WasmGrpcProvider, WasmProvider}; @@ -204,10 +204,10 @@ impl CosmosProvider { /// Extract contract address from transaction. fn contract(tx: &Tx, tx_hash: &H256) -> ChainResult { // We merge two error messages together so that both of them are reported - match Self::contract_address_from_msg_execute_contract(tx, tx_hash) { + match Self::contract_address_from_msg_execute_contract(tx) { Ok(contract) => Ok(contract), Err(msg_execute_contract_error) => { - match Self::contract_address_from_msg_recv_packet(tx, tx_hash) { + match Self::contract_address_from_msg_recv_packet(tx) { Ok(contract) => Ok(contract), Err(msg_recv_packet_error) => { let errors = vec![msg_execute_contract_error, msg_recv_packet_error]; @@ -221,10 +221,7 @@ impl CosmosProvider { } /// Assumes that there is only one `MsgExecuteContract` message in the transaction - fn contract_address_from_msg_execute_contract( - tx: &Tx, - tx_hash: &H256, - ) -> Result { + fn contract_address_from_msg_execute_contract(tx: &Tx) -> Result { use cosmrs::proto::cosmwasm::wasm::v1::MsgExecuteContract as ProtoMsgExecuteContract; let contract_execution_messages = tx @@ -253,10 +250,7 @@ impl CosmosProvider { Ok(contract) } - fn contract_address_from_msg_recv_packet( - tx: &Tx, - tx_hash: &H256, - ) -> Result { + fn contract_address_from_msg_recv_packet(tx: &Tx) -> Result { let packet_data = tx .body .messages @@ -392,7 +386,9 @@ impl HyperlaneProvider for CosmosProvider { Ok(block_info) } - async fn get_txn_by_hash(&self, hash: &H256) -> ChainResult { + async fn get_txn_by_hash(&self, hash: &H512) -> ChainResult { + let hash: H256 = H256::from_slice(&h512_to_bytes(hash)); + let tendermint_hash = Hash::from_bytes(Algorithm::Sha256, hash.as_bytes()) .expect("transaction hash should be of correct size"); @@ -400,7 +396,7 @@ impl HyperlaneProvider for CosmosProvider { let received_hash = H256::from_slice(response.hash.as_bytes()); - if &received_hash != hash { + if received_hash != hash { return Err(ChainCommunicationError::from_other_str(&format!( "received incorrect transaction, expected hash: {:?}, received hash: {:?}", hash, received_hash, @@ -409,12 +405,12 @@ impl HyperlaneProvider for CosmosProvider { let tx = Tx::from_bytes(&response.tx)?; - let contract = Self::contract(&tx, hash)?; + let contract = Self::contract(&tx, &hash)?; let (sender, nonce) = self.sender_and_nonce(&tx)?; - let gas_price = self.calculate_gas_price(hash, &tx); + let gas_price = self.calculate_gas_price(&hash, &tx); let tx_info = TxnInfo { - hash: hash.to_owned(), + hash: hash.into(), gas_limit: U256::from(response.tx_result.gas_wanted), max_priority_fee_per_gas: None, max_fee_per_gas: None, diff --git a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs index c8ef3d78ce..d89cf9f43f 100644 --- a/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs @@ -7,7 +7,7 @@ use async_trait::async_trait; use derive_new::new; use ethers::prelude::Middleware; use ethers_core::{abi::Address, types::BlockNumber}; -use hyperlane_core::{ethers_core_types, ChainInfo, HyperlaneCustomErrorWrapper, U256}; +use hyperlane_core::{ethers_core_types, ChainInfo, HyperlaneCustomErrorWrapper, H512, U256}; use tokio::time::sleep; use tracing::instrument; @@ -84,7 +84,7 @@ where #[instrument(err, skip(self))] #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue - async fn get_txn_by_hash(&self, hash: &H256) -> ChainResult { + async fn get_txn_by_hash(&self, hash: &H512) -> ChainResult { let txn = get_with_retry_on_none( hash, |h| self.provider.get_transaction(*h), diff --git a/rust/main/chains/hyperlane-fuel/src/provider.rs b/rust/main/chains/hyperlane-fuel/src/provider.rs index a72f8a6c5f..cdd32650e1 100644 --- a/rust/main/chains/hyperlane-fuel/src/provider.rs +++ b/rust/main/chains/hyperlane-fuel/src/provider.rs @@ -17,9 +17,9 @@ use fuels::{ }; use futures::future::join_all; use hyperlane_core::{ - BlockInfo, ChainCommunicationError, ChainInfo, ChainResult, HyperlaneChain, HyperlaneDomain, - HyperlaneMessage, HyperlaneProvider, HyperlaneProviderError, Indexed, LogMeta, TxnInfo, H256, - H512, U256, + h512_to_bytes, BlockInfo, ChainCommunicationError, ChainInfo, ChainResult, HyperlaneChain, + HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, HyperlaneProviderError, Indexed, LogMeta, + TxnInfo, H256, H512, U256, }; use crate::{make_client, make_provider, prelude::FuelIntoH256, ConnectionConf}; @@ -314,7 +314,9 @@ impl HyperlaneProvider for FuelProvider { /// Used by scraper #[allow(clippy::clone_on_copy)] // TODO: `rustc` 1.80.1 clippy issue #[allow(clippy::match_like_matches_macro)] // TODO: `rustc` 1.80.1 clippy issue - async fn get_txn_by_hash(&self, hash: &H256) -> ChainResult { + async fn get_txn_by_hash(&self, hash: &H512) -> ChainResult { + let hash = H256::from_slice(&h512_to_bytes(hash)); + let transaction_res = self .provider .get_transaction_by_id(&hash.0.into()) @@ -370,7 +372,7 @@ impl HyperlaneProvider for FuelProvider { }; Ok(TxnInfo { - hash: hash.clone(), + hash: hash.into(), gas_limit: gas_limit.into(), max_priority_fee_per_gas: None, max_fee_per_gas: None, diff --git a/rust/main/chains/hyperlane-sealevel/src/provider.rs b/rust/main/chains/hyperlane-sealevel/src/provider.rs index aaf32ac593..932a7191ea 100644 --- a/rust/main/chains/hyperlane-sealevel/src/provider.rs +++ b/rust/main/chains/hyperlane-sealevel/src/provider.rs @@ -3,7 +3,7 @@ use std::{str::FromStr, sync::Arc}; use async_trait::async_trait; use hyperlane_core::{ BlockInfo, ChainInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, - HyperlaneProviderError, TxnInfo, H256, U256, + HyperlaneProviderError, TxnInfo, H256, H512, U256, }; use solana_sdk::bs58; use solana_sdk::pubkey::Pubkey; @@ -68,7 +68,7 @@ impl HyperlaneProvider for SealevelProvider { Ok(block_info) } - async fn get_txn_by_hash(&self, _hash: &H256) -> ChainResult { + async fn get_txn_by_hash(&self, _hash: &H512) -> ChainResult { todo!() // FIXME } diff --git a/rust/main/hyperlane-core/src/traits/provider.rs b/rust/main/hyperlane-core/src/traits/provider.rs index 47070bc2ab..63b4d01dd9 100644 --- a/rust/main/hyperlane-core/src/traits/provider.rs +++ b/rust/main/hyperlane-core/src/traits/provider.rs @@ -4,7 +4,7 @@ use async_trait::async_trait; use auto_impl::auto_impl; use thiserror::Error; -use crate::{BlockInfo, ChainInfo, ChainResult, HyperlaneChain, TxnInfo, H256, U256}; +use crate::{BlockInfo, ChainInfo, ChainResult, HyperlaneChain, TxnInfo, H256, H512, U256}; /// Interface for a provider. Allows abstraction over different provider types /// for different chains. @@ -20,7 +20,7 @@ pub trait HyperlaneProvider: HyperlaneChain + Send + Sync + Debug { async fn get_block_by_height(&self, height: u64) -> ChainResult; /// Get txn info for a given txn hash - async fn get_txn_by_hash(&self, hash: &H256) -> ChainResult; + async fn get_txn_by_hash(&self, hash: &H512) -> ChainResult; /// Returns whether a contract exists at the provided address async fn is_contract(&self, address: &H256) -> ChainResult; @@ -40,7 +40,7 @@ pub enum HyperlaneProviderError { NoGasUsed, /// Could not find a transaction by hash #[error("Could not find transaction from provider with hash {0:?}")] - CouldNotFindTransactionByHash(H256), + CouldNotFindTransactionByHash(H512), /// Could not find a block by height #[error("Could not find block from provider with height {0:?}")] CouldNotFindBlockByHeight(u64), diff --git a/rust/main/hyperlane-core/src/traits/signing.rs b/rust/main/hyperlane-core/src/traits/signing.rs index 34e5676c42..5859d3c25b 100644 --- a/rust/main/hyperlane-core/src/traits/signing.rs +++ b/rust/main/hyperlane-core/src/traits/signing.rs @@ -1,10 +1,11 @@ +use std::fmt::{Debug, Formatter}; + use async_trait::async_trait; use auto_impl::auto_impl; use serde::{ ser::{SerializeStruct, Serializer}, Deserialize, Serialize, }; -use std::fmt::{Debug, Formatter}; use crate::utils::bytes_to_hex; use crate::{Signature, H160, H256}; diff --git a/rust/main/hyperlane-core/src/types/chain_data.rs b/rust/main/hyperlane-core/src/types/chain_data.rs index 5f5ecb2e3b..219f6989da 100644 --- a/rust/main/hyperlane-core/src/types/chain_data.rs +++ b/rust/main/hyperlane-core/src/types/chain_data.rs @@ -1,6 +1,6 @@ use derive_new::new; -use crate::{H256, U256}; +use crate::{H256, H512, U256}; /// Info about a given block in the chain. #[derive(Debug, Clone, Default)] @@ -27,7 +27,7 @@ pub struct ChainInfo { #[derive(Debug, Clone)] pub struct TxnInfo { /// Hash of this transaction - pub hash: H256, + pub hash: H512, /// Amount of gas which was allocated for running the transaction pub gas_limit: U256, /// Represents the maximum tx fee that will go to the miner as part of the diff --git a/rust/main/hyperlane-core/src/types/conversions.rs b/rust/main/hyperlane-core/src/types/conversions.rs new file mode 100644 index 0000000000..5336e0e2bf --- /dev/null +++ b/rust/main/hyperlane-core/src/types/conversions.rs @@ -0,0 +1,138 @@ +use uint::unroll; + +use crate::{H256, H512}; + +/// Creates a big-endian hex representation of the address +pub fn address_to_bytes(data: &H256) -> Vec { + if is_h160(data.as_fixed_bytes()) { + // take the last 20 bytes + data.as_fixed_bytes()[12..32].into() + } else { + h256_to_bytes(data) + } +} + +/// Creates a big-endian hex representation of the address +pub fn bytes_to_address(data: Vec) -> eyre::Result { + if (data.len() != 20) && (data.len() != 32) { + return Err(eyre::eyre!("Invalid address length")); + } + if data.len() == 20 { + let mut prefix = vec![0; 12]; + prefix.extend(data); + Ok(H256::from_slice(&prefix[..])) + } else { + Ok(H256::from_slice(&data[..])) + } +} + +/// Creates a big-endian hex representation of the address hash +pub fn h256_to_bytes(data: &H256) -> Vec { + data.as_fixed_bytes().as_slice().into() +} + +/// Creates a big-endian hex representation of the address hash +pub fn h512_to_bytes(data: &H512) -> Vec { + if is_h256(data.as_fixed_bytes()) { + // take the last 32 bytes + data.as_fixed_bytes()[32..64].into() + } else { + data.as_fixed_bytes().as_slice().into() + } +} + +/// Convert bytes into H512 with padding +pub fn bytes_to_h512(data: &[u8]) -> H512 { + assert!(data.len() <= 64); + + if data.len() == 64 { + return H512::from_slice(data); + } + + let mut buf = [0; 64]; + buf[64 - data.len()..64].copy_from_slice(data); + + H512::from_slice(&buf) +} + +/// Checks if a byte slice fits within 160 bits. Assumes a big-endian encoding; +/// ignores leading zeros. Current implementation only supports up to a 32 byte +/// array but this could easily be extended if needed. +pub const fn is_h160(data: &[u8; S]) -> bool { + assert!(S <= 32); + if S <= 20 { + true + } else { + let mut z = data[0]; + unroll! { + for i in 0..11 { + if S >= i + 22 { + z |= data[i + 1] + } + } + } + + z == 0 + } +} + +/// Checks if a byte slice fits within 32 bytes. Assumes a big-endian encoding; +/// ignores leading zeros. Current implementation only supports up to a 64 byte long +/// array but this could easily be extended if needed. +pub const fn is_h256(data: &[u8; S]) -> bool { + assert!(S <= 64); + if S <= 32 { + true + } else { + unroll! { + for i in 0..32 { + if data[i] != 0 { + return false; + } + } + } + + true + } +} + +#[cfg(test)] +mod test { + #[test] + fn is_h160() { + let v: [u8; 32] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xd1, + 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, + 0x1c, 0xa8, 0x03, 0x1c, + ]; + assert!(super::is_h160(&v)); + + let v: [u8; 32] = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xd1, + 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, + 0x1c, 0xa8, 0x03, 0x1c, + ]; + assert!(!super::is_h160(&v)); + } + + #[test] + fn is_h256() { + let v: [u8; 64] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xfa, 0xd1, + 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, + 0x1c, 0xa8, 0x03, 0x1c, 0x04, 0x1d, 0x05, 0x1e, + ]; + assert!(super::is_h256(&v)); + + let v: [u8; 64] = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xfa, 0xd1, + 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, + 0x1c, 0xa8, 0x03, 0x1c, 0x04, 0x1d, 0x05, 0x1e, + ]; + assert!(!super::is_h256(&v)); + } +} diff --git a/rust/main/hyperlane-core/src/types/mod.rs b/rust/main/hyperlane-core/src/types/mod.rs index a8973a72b3..84df52d18e 100644 --- a/rust/main/hyperlane-core/src/types/mod.rs +++ b/rust/main/hyperlane-core/src/types/mod.rs @@ -12,6 +12,7 @@ pub use announcement::*; pub use block_id::BlockId; pub use chain_data::*; pub use checkpoint::*; +pub use conversions::*; pub use indexing::*; pub use log_metadata::*; pub use merkle_tree::*; @@ -27,6 +28,7 @@ mod announcement; mod block_id; mod chain_data; mod checkpoint; +mod conversions; mod indexing; mod log_metadata; mod merkle_tree; diff --git a/rust/main/utils/hex/src/lib.rs b/rust/main/utils/hex/src/lib.rs index 46b4d2894f..719a0b7866 100644 --- a/rust/main/utils/hex/src/lib.rs +++ b/rust/main/utils/hex/src/lib.rs @@ -29,27 +29,6 @@ const FROM_HEX_CHARS: [u8; 256] = [ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ]; -/// Checks if a byte slice fits within 160 bits. Assumes a big-endian encoding; -/// ignores leading zeros. Current implementation only supports up to a 32 byte -/// array but this could easily be extended if needed. -pub const fn is_h160(data: &[u8; S]) -> bool { - assert!(S <= 32); - if S <= 20 { - true - } else { - let mut z = data[0]; - unroll! { - for i in 0..11 { - if S >= i + 22 { - z |= data[i + 1] - } - } - } - - z == 0 - } -} - /// This formats a 160bit byte slice as a lowercase hex string without any /// prefixing (will include leading zeros). pub fn format_h160_raw(data: &[u8; 20]) -> String { @@ -150,23 +129,6 @@ impl Error for InvalidHexCharacter {} mod test { use crate::FROM_HEX_CHARS; - #[test] - fn is_h160() { - let v: [u8; 32] = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xd1, - 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, - 0x1c, 0xa8, 0x03, 0x1c, - ]; - assert!(super::is_h160(&v)); - - let v: [u8; 32] = [ - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xd1, - 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, - 0x1c, 0xa8, 0x03, 0x1c, - ]; - assert!(!super::is_h160(&v)); - } - #[test] fn format_h160() { let v: [u8; 20] = [ diff --git a/rust/sealevel/environments/mainnet3/eclipsemainnet/gas-oracle-configs-eclipsemainnet.json b/rust/sealevel/environments/mainnet3/eclipsemainnet/gas-oracle-configs-eclipsemainnet.json index 95b108c96c..b459e88d4c 100644 --- a/rust/sealevel/environments/mainnet3/eclipsemainnet/gas-oracle-configs-eclipsemainnet.json +++ b/rust/sealevel/environments/mainnet3/eclipsemainnet/gas-oracle-configs-eclipsemainnet.json @@ -1,20 +1,29 @@ [ - { - "domain": 1, - "gasOracle": { - "type": "remoteGasData", - "tokenExchangeRate": "15000000000000000000", - "gasPrice": "10000000000", - "tokenDecimals": 18 - } - }, - { - "domain": 1399811149, - "gasOracle": { - "type": "remoteGasData", - "tokenExchangeRate": "887000000000000000", - "gasPrice": "15", - "tokenDecimals": 9 - } + { + "domain": 1, + "gasOracle": { + "type": "remoteGasData", + "tokenExchangeRate": "15000000000000000000", + "gasPrice": "10000000000", + "tokenDecimals": 18 } + }, + { + "domain": 1399811149, + "gasOracle": { + "type": "remoteGasData", + "tokenExchangeRate": "887000000000000000", + "gasPrice": "15", + "tokenDecimals": 9 + } + }, + { + "domain": 745, + "gasOracle": { + "type": "remoteGasData", + "tokenExchangeRate": "435246388284187", + "gasPrice": "7", + "tokenDecimals": 6 + } + } ] diff --git a/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/program-ids.json new file mode 100644 index 0000000000..6f10e10143 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/program-ids.json @@ -0,0 +1,10 @@ +{ + "stride": { + "hex": "0x0b1798722ccf813f617ffa8d52d36302544807497434740d98e693e9abb245d1", + "base58": "kJMTJuh4tzhzfTV261wh2cisqb7NNev6ZE6czwB3ss6" + }, + "eclipsemainnet": { + "hex": "0xa0c167513f4d025217a48891973c3dbe41e10e76230033ef5d676299a18ca7f5", + "base58": "BpXHAiktwjx7fN6M9ST9wr6qKAsH27wZFhdHEhReJsR6" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/token-config.json new file mode 100644 index 0000000000..f501f4ee81 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/token-config.json @@ -0,0 +1,17 @@ +{ + "eclipsemainnet": { + "type": "synthetic", + "decimals": 6, + "remoteDecimals": 6, + "name": "Celestia", + "symbol": "TIA", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/b21b78514a42e8c050b36472c8325fd4b5177366/deployments/warp_routes/TIA/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj" + }, + "stride": { + "type": "collateral", + "decimals": 6, + "token": "ibc/BF3B4F53F3694B66E13C23107C84B6485BD2B96296BB7EC680EA77BBA75B4801", + "foreignDeployment": "0x0b1798722ccf813f617ffa8d52d36302544807497434740d98e693e9abb245d1" + } +} diff --git a/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/program-ids.json new file mode 100644 index 0000000000..1af852d361 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/program-ids.json @@ -0,0 +1,10 @@ +{ + "stride": { + "hex": "0x8d7a6737fdd9545dc77d1c5cc2d26cb1321f6c67f0facb030149da9cc58f0cbe", + "base58": "AXGjtKVpzYdXYX155z6qYQC4Up7fi5LPKNXAK32gi3x9" + }, + "eclipsemainnet": { + "hex": "0x0d258188d0761163da174da890d0c1becdee51a01dbc9e2a6bfcb342140eb509", + "base58": "tKUHyJ5NxhnwU94JUmzh1ekukDcHHX8mZF6fqxbMwX6" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/token-config.json new file mode 100644 index 0000000000..c59c12bb6c --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/token-config.json @@ -0,0 +1,17 @@ +{ + "eclipsemainnet": { + "type": "synthetic", + "decimals": 6, + "remoteDecimals": 6, + "name": "Stride Staked TIA", + "symbol": "stTIA", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/dee58183e51f4eb43e84dbac0e595a4b389dbe80/deployments/warp_routes/stTIA/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj" + }, + "stride": { + "type": "collateral", + "decimals": 6, + "token": "stutia", + "foreignDeployment": "0x8d7a6737fdd9545dc77d1c5cc2d26cb1321f6c67f0facb030149da9cc58f0cbe" + } +} diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index fbecf3809c..3bbfb9ebce 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/core +## 5.6.1 + +### Patch Changes + +- a42616ff3: Added overrides for transferFrom, totalSupply to reflect the internal share based accounting for the 4626 mirror asset +- Updated dependencies [5fd4267e7] +- Updated dependencies [a36fc5fb2] + - @hyperlane-xyz/utils@5.6.2 + ## 5.6.0 ### Minor Changes diff --git a/solidity/contracts/PackageVersioned.sol b/solidity/contracts/PackageVersioned.sol index b1fa39c8ab..44913c2d9c 100644 --- a/solidity/contracts/PackageVersioned.sol +++ b/solidity/contracts/PackageVersioned.sol @@ -7,5 +7,5 @@ pragma solidity >=0.6.11; **/ abstract contract PackageVersioned { // GENERATED CODE - DO NOT EDIT - string public constant PACKAGE_VERSION = "5.6.0"; + string public constant PACKAGE_VERSION = "5.6.1"; } diff --git a/solidity/package.json b/solidity/package.json index f8cbe196c9..5f7eef4420 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "5.6.0", + "version": "5.6.1", "dependencies": { "@arbitrum/nitro-contracts": "^1.2.1", "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "5.6.1", + "@hyperlane-xyz/utils": "5.6.2", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@openzeppelin/contracts": "^4.9.3", "@openzeppelin/contracts-upgradeable": "^v4.9.3", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index 08febc36fb..96e8357459 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 5.6.2 + ## 5.6.1 ## 5.6.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index 5a7c9cdf16..35442643cc 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "5.6.1", + "version": "5.6.2", "description": "CCIP server", "typings": "dist/index.d.ts", "typedocMain": "src/index.ts", diff --git a/typescript/cli/CHANGELOG.md b/typescript/cli/CHANGELOG.md index 37fc5fea7a..dfe8451c34 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/cli +## 5.6.2 + +### Patch Changes + +- Updated dependencies [5fd4267e7] +- Updated dependencies [a36fc5fb2] + - @hyperlane-xyz/utils@5.6.2 + - @hyperlane-xyz/sdk@5.6.2 + ## 5.6.1 ### Patch Changes diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 9348646b80..50d2d4f49c 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,13 +1,13 @@ { "name": "@hyperlane-xyz/cli", - "version": "5.6.1", + "version": "5.6.2", "description": "A command-line utility for common Hyperlane operations", "dependencies": { "@aws-sdk/client-kms": "^3.577.0", "@aws-sdk/client-s3": "^3.577.0", "@hyperlane-xyz/registry": "4.7.0", - "@hyperlane-xyz/sdk": "5.6.1", - "@hyperlane-xyz/utils": "5.6.1", + "@hyperlane-xyz/sdk": "5.6.2", + "@hyperlane-xyz/utils": "5.6.2", "@inquirer/core": "9.0.10", "@inquirer/figures": "1.0.5", "@inquirer/prompts": "^3.0.0", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index 8937a2bf5f..087c0bddb8 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '5.6.1'; +export const VERSION = '5.6.2'; diff --git a/typescript/github-proxy/CHANGELOG.md b/typescript/github-proxy/CHANGELOG.md index ae6e2a41af..d23057e7df 100644 --- a/typescript/github-proxy/CHANGELOG.md +++ b/typescript/github-proxy/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/github-proxy +## 5.6.2 + ## 5.6.1 ## 5.6.0 diff --git a/typescript/github-proxy/package.json b/typescript/github-proxy/package.json index 9f804e01d2..28d122eb48 100644 --- a/typescript/github-proxy/package.json +++ b/typescript/github-proxy/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/github-proxy", "description": "Github proxy that adds the API key to requests", - "version": "5.6.1", + "version": "5.6.2", "private": true, "scripts": { "deploy": "wrangler deploy", diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index 17f77d3d57..171117f07c 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/helloworld +## 5.6.2 + +### Patch Changes + +- Updated dependencies [5fd4267e7] +- Updated dependencies [a42616ff3] + - @hyperlane-xyz/sdk@5.6.2 + - @hyperlane-xyz/core@5.6.1 + ## 5.6.1 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index bc404b472b..722eed273c 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/helloworld", "description": "A basic skeleton of an Hyperlane app", - "version": "5.6.1", + "version": "5.6.2", "dependencies": { - "@hyperlane-xyz/core": "5.6.0", + "@hyperlane-xyz/core": "5.6.1", "@hyperlane-xyz/registry": "4.7.0", - "@hyperlane-xyz/sdk": "5.6.1", + "@hyperlane-xyz/sdk": "5.6.2", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index f5f25b3f32..eee6156256 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,15 @@ # @hyperlane-xyz/infra +## 5.6.2 + +### Patch Changes + +- Updated dependencies [5fd4267e7] +- Updated dependencies [a36fc5fb2] + - @hyperlane-xyz/utils@5.6.2 + - @hyperlane-xyz/sdk@5.6.2 + - @hyperlane-xyz/helloworld@5.6.2 + ## 5.6.1 ### Patch Changes diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index d6999cd18e..68ac9ccb9a 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -429,7 +429,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '42d6b50-20241021-155906', + tag: 'a64af8b-20241024-120818', }, gasPaymentEnforcement: gasPaymentEnforcement, metricAppContexts, @@ -438,7 +438,7 @@ const hyperlane: RootAgentConfig = { validators: { docker: { repo, - tag: 'efd438f-20241016-101828', + tag: 'a64af8b-20241024-120818', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.Hyperlane), @@ -448,7 +448,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '42d6b50-20241021-155906', + tag: 'a64af8b-20241024-120818', }, resources: scraperResources, }, @@ -463,7 +463,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '42d6b50-20241021-155906', + tag: 'a64af8b-20241024-120818', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. @@ -475,7 +475,7 @@ const releaseCandidate: RootAgentConfig = { validators: { docker: { repo, - tag: '9c056c7-20240911-154357', + tag: 'a64af8b-20241024-120818', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.ReleaseCandidate), diff --git a/typescript/infra/config/environments/mainnet3/warp/EZETH-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/EZETH-deployments.yaml index b836636457..26a0a5807d 100644 --- a/typescript/infra/config/environments/mainnet3/warp/EZETH-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/EZETH-deployments.yaml @@ -10,6 +10,7 @@ data: symbol: ezETH hypAddress: '0xC59336D8edDa9722B4f1Ec104007191Ec16f7087' tokenAddress: '0xbf5495Efe5DB9ce00f80364C8B423567e58d2110' + tokenCoinGeckoId: renzo-restaked-eth decimals: 18 bsc: protocolType: ethereum diff --git a/typescript/infra/config/environments/mainnet3/warp/STTIA-eclipsestride-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/STTIA-eclipsestride-deployments.yaml new file mode 100644 index 0000000000..9938ad9f98 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/STTIA-eclipsestride-deployments.yaml @@ -0,0 +1,23 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +description: Hyperlane Warp Route artifacts +timestamp: '2024-10-18T14:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + stride: + protocolType: cosmos + type: collateral + hypAddress: stride134axwdlam929m3mar3wv95nvkyep7mr87ravkqcpf8dfe3v0pjlqwrw6ee + tokenAddress: 'stutia' + name: Stride Staked TIA + symbol: stTIA + decimals: 6 + eclipsemainnet: + protocolType: sealevel + type: synthetic + hypAddress: 'tKUHyJ5NxhnwU94JUmzh1ekukDcHHX8mZF6fqxbMwX6' + tokenAddress: 'V5m1Cc9VK61mKL8xVYrjR7bjD2BC5VpADLa6ws3G8KM' + isSpl2022: true + name: Turbo Eth + symbol: tETH + decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/TIA-eclipsestride-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/TIA-eclipsestride-deployments.yaml new file mode 100644 index 0000000000..999e8ea951 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/TIA-eclipsestride-deployments.yaml @@ -0,0 +1,23 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +description: Hyperlane Warp Route artifacts +timestamp: '2024-10-18T14:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + stride: + protocolType: cosmos + type: collateral + hypAddress: stride1pvtesu3ve7qn7ctll2x495mrqf2ysp6fws68grvcu6f7n2ajghgsh2jdj6 + tokenAddress: ibc/BF3B4F53F3694B66E13C23107C84B6485BD2B96296BB7EC680EA77BBA75B4801 + name: Celestia + symbol: TIA + decimals: 6 + eclipsemainnet: + protocolType: sealevel + type: synthetic + hypAddress: 'BpXHAiktwjx7fN6M9ST9wr6qKAsH27wZFhdHEhReJsR6' + tokenAddress: '9RryNMhAVJpAwAGjCAMKbbTFwgjapqPkzpGMfTQhEjf8' + isSpl2022: true + name: Turbo Eth + symbol: tETH + decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/ancient8-USDC-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/ancient8-USDC-deployments.yaml index 07be060273..d87b3fa0a7 100644 --- a/typescript/infra/config/environments/mainnet3/warp/ancient8-USDC-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/ancient8-USDC-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0x8b4192B9Ad1fCa440A5808641261e5289e6de95D' tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' # USDC + tokenCoinGeckoId: usd-coin name: USDC symbol: USDC decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/bsc-lumia-LUMIA-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/bsc-lumia-LUMIA-deployments.yaml index 9db075248d..d106ac884e 100644 --- a/typescript/infra/config/environments/mainnet3/warp/bsc-lumia-LUMIA-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/bsc-lumia-LUMIA-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0xdD313D475f8A9d81CBE2eA953a357f52e10BA357' tokenAddress: '0xd9343a049d5dbd89cd19dc6bca8c48fb3a0a42a7' + tokenCoinGeckoId: lumia name: Lumia Token symbol: LUMIA decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideSTTIAWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideSTTIAWarpConfig.ts new file mode 100644 index 0000000000..eaf935f9bb --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideSTTIAWarpConfig.ts @@ -0,0 +1,29 @@ +import { + ChainMap, + RouterConfig, + TokenRouterConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; + +export const getEclipseStrideTiaWarpConfig = async ( + _routerConfig: ChainMap, +): Promise> => { + // @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig + const eclipsemainnet: TokenRouterConfig = { + type: TokenType.synthetic, + foreignDeployment: 'BpXHAiktwjx7fN6M9ST9wr6qKAsH27wZFhdHEhReJsR6', + gas: 300_000, + }; + + // @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig + const stride: TokenRouterConfig = { + type: TokenType.collateral, + foreignDeployment: + 'stride1pvtesu3ve7qn7ctll2x495mrqf2ysp6fws68grvcu6f7n2ajghgsh2jdj6', + }; + + return { + eclipsemainnet, + stride, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideTIAWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideTIAWarpConfig.ts new file mode 100644 index 0000000000..aa2cf6a103 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideTIAWarpConfig.ts @@ -0,0 +1,29 @@ +import { + ChainMap, + RouterConfig, + TokenRouterConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; + +export const getEclipseStrideStTiaWarpConfig = async ( + _routerConfig: ChainMap, +): Promise> => { + // @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig + const eclipsemainnet: TokenRouterConfig = { + type: TokenType.synthetic, + foreignDeployment: 'tKUHyJ5NxhnwU94JUmzh1ekukDcHHX8mZF6fqxbMwX6', + gas: 300_000, + }; + + // @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig + const stride: TokenRouterConfig = { + type: TokenType.collateral, + foreignDeployment: + 'stride134axwdlam929m3mar3wv95nvkyep7mr87ravkqcpf8dfe3v0pjlqwrw6ee', + }; + + return { + eclipsemainnet, + stride, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/eclipse-SOL-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/eclipse-SOL-deployments.yaml index 0469f45ebc..711e6d4282 100644 --- a/typescript/infra/config/environments/mainnet3/warp/eclipse-SOL-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/eclipse-SOL-deployments.yaml @@ -9,6 +9,7 @@ data: protocolType: sealevel type: native hypAddress: '8DtAGQpcMuD5sG3KdxDy49ydqXUggR1LQtebh2TECbAc' + tokenCoinGeckoId: solana name: Solana symbol: SOL decimals: 9 diff --git a/typescript/infra/config/environments/mainnet3/warp/eclipse-USDC-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/eclipse-USDC-deployments.yaml index 2955d2c84b..7405cb94d2 100644 --- a/typescript/infra/config/environments/mainnet3/warp/eclipse-USDC-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/eclipse-USDC-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0xe1De9910fe71cC216490AC7FCF019e13a34481D7' tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' # USDC + tokenCoinGeckoId: usd-coin name: USDC symbol: USDC decimals: 6 @@ -18,6 +19,7 @@ data: type: collateral hypAddress: '3EpVCPUgyjq2MfGeCttyey6bs5zya5wjYZ2BE6yDg6bm' tokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' + tokenCoinGeckoId: usd-coin isSpl2022: false name: USDC symbol: USDC diff --git a/typescript/infra/config/environments/mainnet3/warp/eclipse-WIF-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/eclipse-WIF-deployments.yaml index 94e44a8b1a..1b7245c91c 100644 --- a/typescript/infra/config/environments/mainnet3/warp/eclipse-WIF-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/eclipse-WIF-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: 'CuQmsT4eSF4dYiiGUGYYQxJ7c58pUAD5ADE3BbFGzQKx' tokenAddress: 'EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm' + tokenCoinGeckoId: dogwifcoin name: dogwifhat symbol: WIF decimals: 9 diff --git a/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml index 0d8ff77557..90f22e0dff 100644 --- a/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0xED56728fb977b0bBdacf65bCdD5e17Bb7e84504f' tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' # USDC + tokenCoinGeckoId: usd-coin name: USDC symbol: USDC decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml index 25ec599f73..91976ddd98 100644 --- a/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0xab852e67bf03E74C89aF67C4BA97dd1088D3dA19' tokenAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7' # USDT + tokenCoinGeckoId: tether name: Tether USD symbol: USDT decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml index efabaf59da..cd39382ea3 100644 --- a/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml @@ -9,6 +9,7 @@ data: protocolType: cosmos type: native hypAddress: inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k + tokenCoinGeckoId: injective-protocol name: Injective Coin symbol: INJ decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml index ae13acf3d7..bf4e1770ae 100644 --- a/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: neutron1ch7x3xgpnj62weyes8vfada35zff6z59kt2psqhnx9gjnt2ttqdqtva3pa tokenAddress: ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7 + tokenCoinGeckoId: celestia name: Celestia symbol: TIA decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/sei-FASTUSD-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/sei-FASTUSD-deployments.yaml index 885c1a4dbd..ed9672e416 100644 --- a/typescript/infra/config/environments/mainnet3/warp/sei-FASTUSD-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/sei-FASTUSD-deployments.yaml @@ -8,6 +8,7 @@ data: type: collateral hypAddress: '0x9AD81058c6C3Bf552C9014CB30E824717A0ee21b' tokenAddress: '0x15700B564Ca08D9439C58cA5053166E8317aa138' + tokenCoinGeckoId: elixir-deusd # unique setup where we want deUSD to be deposited as collateral and we want fastUSD to be minted as a synthetic on sei name: fastUSD symbol: fastUSD decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml index 8923facc00..c3e03b54d5 100644 --- a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml @@ -9,6 +9,7 @@ data: protocolType: ethereum type: native hypAddress: '0x15b5D6B614242B118AA404528A7f3E2Ad241e4A4' + tokenCoinGeckoId: ethereum name: Ether symbol: ETH decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml index a278a93e8c..8a84e6a7c5 100644 --- a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0x31Dca7762930f56D81292f85E65c9D67575804fE' tokenAddress: '0x31Dca7762930f56D81292f85E65c9D67575804fE' # USDC + tokenCoinGeckoId: usd-coin name: USD Coin symbol: USDC decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml index 8ddaa1e085..a225980420 100644 --- a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0x4221a16A01F61c2b38A03C52d828a7041f6AAA49' tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7' # USDT + tokenCoinGeckoId: tether name: Tether USD symbol: USDT decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts index debc81326c..a90601c356 100644 --- a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -6,6 +6,8 @@ export enum WarpRouteIds { ArbitrumNeutronTIA = 'TIA/arbitrum-neutron', EclipseSolanaSOL = 'SOL/eclipsemainnet-solanamainnet', EclipseSolanaWIF = 'WIF/eclipsemainnet-solanamainnet', + EclipseStrideSTTIA = 'stTIA/eclipse-stride', + EclipseStrideTIA = 'TIA/eclipse-stride', EthereumInevmUSDC = 'USDC/ethereum-inevm', EthereumInevmUSDT = 'USDT/ethereum-inevm', EthereumEclipseTETH = 'tETH/eclipsemainnet-ethereum', diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index 2b7c44ee40..84086342e7 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -12,6 +12,8 @@ import { getAncient8EthereumUSDCWarpConfig } from './environments/mainnet3/warp/ import { getArbitrumEthereumZircuitAmphrETHWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumEthereumZircuitAmphrETHWarpConfig.js'; import { getArbitrumNeutronEclipWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronEclipWarpConfig.js'; import { getArbitrumNeutronTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.js'; +import { getEclipseStrideTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseStrideSTTIAWarpConfig.js'; +import { getEclipseStrideStTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseStrideTIAWarpConfig.js'; import { getEthereumInevmUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDCWarpConfig.js'; import { getEthereumInevmUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDTWarpConfig.js'; import { getEthereumSeiFastUSDWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumSeiFastUSDWarpConfig.js'; @@ -50,6 +52,8 @@ export const warpConfigGetterMap: Record< [WarpRouteIds.EthereumVictionUSDT]: getEthereumVictionUSDTWarpConfig, [WarpRouteIds.EthereumZircuitPZETH]: getRenzoPZETHWarpConfig, [WarpRouteIds.MantapacificNeutronTIA]: getMantapacificNeutronTiaWarpConfig, + [WarpRouteIds.EclipseStrideTIA]: getEclipseStrideTiaWarpConfig, + [WarpRouteIds.EclipseStrideSTTIA]: getEclipseStrideStTiaWarpConfig, }; export async function getWarpConfig( diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 375d06c59d..c5fb41ac25 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/infra", "description": "Infrastructure utilities for the Hyperlane Network", - "version": "5.6.1", + "version": "5.6.2", "dependencies": { "@arbitrum/sdk": "^3.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "5.6.1", - "@hyperlane-xyz/registry": "4.7.0", - "@hyperlane-xyz/sdk": "5.6.1", - "@hyperlane-xyz/utils": "5.6.1", + "@hyperlane-xyz/helloworld": "5.6.2", + "@hyperlane-xyz/registry": "4.10.0", + "@hyperlane-xyz/sdk": "5.6.2", + "@hyperlane-xyz/utils": "5.6.2", "@inquirer/prompts": "^5.3.8", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "1.3.0", diff --git a/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts b/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts index 3d946a9e68..2d588a89c2 100644 --- a/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts +++ b/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts @@ -9,10 +9,12 @@ import { IXERC20__factory, } from '@hyperlane-xyz/core'; import { ERC20__factory } from '@hyperlane-xyz/core'; +import { createWarpRouteConfigId } from '@hyperlane-xyz/registry'; import { ChainMap, ChainMetadata, ChainName, + CoinGeckoTokenPriceGetter, CosmNativeTokenAdapter, CwNativeTokenAdapter, MultiProtocolProvider, @@ -38,17 +40,41 @@ import { getEnvironmentConfig } from '../core-utils.js'; const logger = rootLogger.child({ module: 'warp-balance-monitor' }); const metricsRegister = new Registry(); + +interface WarpRouteMetrics { + chain_name: ChainName; + token_address: string; + token_name: string; + wallet_address: string; + token_type: TokenType; + warp_route_id: string; + related_chain_names: string; +} + +type WarpRouteMetricLabels = keyof WarpRouteMetrics; + +const warpRouteMetricLabels: WarpRouteMetricLabels[] = [ + 'chain_name', + 'token_address', + 'token_name', + 'wallet_address', + 'token_type', + 'warp_route_id', + 'related_chain_names', +]; + const warpRouteTokenBalance = new Gauge({ name: 'hyperlane_warp_route_token_balance', help: 'HypERC20 token balance of a Warp Route', registers: [metricsRegister], - labelNames: [ - 'chain_name', - 'token_address', - 'token_name', - 'wallet_address', - 'token_type', - ], + labelNames: warpRouteMetricLabels, +}); + +const warpRouteCollateralValue = new Gauge({ + name: 'hyperlane_warp_route_collateral_value', + help: 'Total value of collateral held in a HypERC20Collateral or HypNative contract of a Warp Route', + registers: [metricsRegister], + labelNames: warpRouteMetricLabels, }); const xERC20LimitsGauge = new Gauge({ @@ -66,6 +92,11 @@ interface xERC20Limit { burnMax: number; } +interface WarpRouteInfo { + balance: number; + valueUSD?: number; +} + export function readWarpRouteConfig(filePath: string) { const config = readYaml(filePath); if (!config) throw new Error(`No warp config found at ${filePath}`); @@ -112,7 +143,8 @@ async function main(): Promise { async function checkBalance( tokenConfig: WarpRouteConfig, multiProtocolProvider: MultiProtocolProvider, -): Promise> { + tokenPriceGetter: CoinGeckoTokenPriceGetter, +): Promise> { const output = objMap( tokenConfig, async (chain: ChainName, token: WarpRouteConfig[ChainName]) => { @@ -122,8 +154,12 @@ async function checkBalance( case ProtocolType.Ethereum: { const provider = multiProtocolProvider.getEthersV5Provider(chain); const nativeBalance = await provider.getBalance(token.hypAddress); - return parseFloat( - ethers.utils.formatUnits(nativeBalance, token.decimals), + + return getNativeTokenWarpInfo( + nativeBalance, + token.decimals, + tokenPriceGetter, + chain, ); } case ProtocolType.Sealevel: { @@ -142,8 +178,12 @@ async function checkBalance( const balance = ethers.BigNumber.from( await adapter.getBalance(token.hypAddress), ); - return parseFloat( - ethers.utils.formatUnits(balance, token.decimals), + + return getNativeTokenWarpInfo( + balance, + token.decimals, + tokenPriceGetter, + chain, ); } case ProtocolType.Cosmos: { @@ -156,8 +196,12 @@ async function checkBalance( { ibcDenom: token.ibcDenom }, ); const tokenBalance = await adapter.getBalance(token.hypAddress); - return parseFloat( - ethers.utils.formatUnits(tokenBalance, token.decimals), + + return getNativeTokenWarpInfo( + tokenBalance, + token.decimals, + tokenPriceGetter, + chain, ); } } @@ -177,8 +221,11 @@ async function checkBalance( token.hypAddress, ); - return parseFloat( - ethers.utils.formatUnits(collateralBalance, token.decimals), + return getCollateralTokenWarpInfo( + collateralBalance, + token.decimals, + tokenPriceGetter, + token.tokenCoinGeckoId, ); } case ProtocolType.Sealevel: { @@ -198,8 +245,12 @@ async function checkBalance( const collateralBalance = ethers.BigNumber.from( await adapter.getBalance(token.hypAddress), ); - return parseFloat( - ethers.utils.formatUnits(collateralBalance, token.decimals), + + return getCollateralTokenWarpInfo( + collateralBalance, + token.decimals, + tokenPriceGetter, + token.tokenCoinGeckoId, ); } case ProtocolType.Cosmos: { @@ -216,8 +267,12 @@ async function checkBalance( const collateralBalance = ethers.BigNumber.from( await adapter.getBalance(token.hypAddress), ); - return parseFloat( - ethers.utils.formatUnits(collateralBalance, token.decimals), + + return getCollateralTokenWarpInfo( + collateralBalance, + token.decimals, + tokenPriceGetter, + token.tokenCoinGeckoId, ); } } @@ -232,9 +287,11 @@ async function checkBalance( provider, ); const syntheticBalance = await tokenContract.totalSupply(); - return parseFloat( - ethers.utils.formatUnits(syntheticBalance, token.decimals), - ); + return { + balance: parseFloat( + ethers.utils.formatUnits(syntheticBalance, token.decimals), + ), + }; } case ProtocolType.Sealevel: { if (!token.tokenAddress) @@ -253,13 +310,15 @@ async function checkBalance( const syntheticBalance = ethers.BigNumber.from( await adapter.getTotalSupply(), ); - return parseFloat( - ethers.utils.formatUnits(syntheticBalance, token.decimals), - ); + return { + balance: parseFloat( + ethers.utils.formatUnits(syntheticBalance, token.decimals), + ), + }; } case ProtocolType.Cosmos: // TODO - cosmos synthetic - return 0; + return { balance: 0 }; } break; } @@ -275,9 +334,11 @@ async function checkBalance( const xerc20 = IXERC20__factory.connect(xerc20Address, provider); const syntheticBalance = await xerc20.totalSupply(); - return parseFloat( - ethers.utils.formatUnits(syntheticBalance, token.decimals), - ); + return { + balance: parseFloat( + ethers.utils.formatUnits(syntheticBalance, token.decimals), + ), + }; } default: throw new Error( @@ -307,8 +368,11 @@ async function checkBalance( xerc20LockboxAddress, ); - return parseFloat( - ethers.utils.formatUnits(collateralBalance, token.decimals), + return getCollateralTokenWarpInfo( + collateralBalance, + token.decimals, + tokenPriceGetter, + token.tokenCoinGeckoId, ); } default: @@ -318,7 +382,7 @@ async function checkBalance( } } } - return 0; + return { balance: 0 }; }, ); @@ -327,22 +391,44 @@ async function checkBalance( export function updateTokenBalanceMetrics( tokenConfig: WarpRouteConfig, - balances: ChainMap, + balances: ChainMap, ) { objMap(tokenConfig, (chain: ChainName, token: WarpRouteConfig[ChainName]) => { - warpRouteTokenBalance - .labels({ - chain_name: chain, - token_address: token.tokenAddress ?? ethers.constants.AddressZero, - token_name: token.name, - wallet_address: token.hypAddress, - token_type: token.type, - }) - .set(balances[chain]); + const metrics: WarpRouteMetrics = { + chain_name: chain, + token_address: token.tokenAddress ?? ethers.constants.AddressZero, + token_name: token.name, + wallet_address: token.hypAddress, + token_type: token.type, + warp_route_id: createWarpRouteConfigId( + token.symbol, + Object.keys(tokenConfig) as ChainName[], + ), + related_chain_names: Object.keys(tokenConfig) + .filter((chainName) => chainName !== chain) + .sort() + .join(','), + }; + + warpRouteTokenBalance.labels(metrics).set(balances[chain].balance); + if (balances[chain].valueUSD) { + warpRouteCollateralValue + .labels(metrics) + .set(balances[chain].valueUSD as number); + logger.debug('Collateral value updated for chain', { + chain, + related_chain_names: metrics.related_chain_names, + warp_route_id: metrics.warp_route_id, + token: metrics.token_name, + value: balances[chain].valueUSD, + }); + } logger.debug('Wallet balance updated for chain', { chain, - token: token.name, - balance: balances[chain], + related_chain_names: metrics.related_chain_names, + warp_route_id: metrics.warp_route_id, + token: metrics.token_name, + balance: balances[chain].balance, }); }); } @@ -468,27 +554,120 @@ const getXERC20Limit = async ( }; }; +async function getTokenPriceByChain( + chain: ChainName, + tokenPriceGetter: CoinGeckoTokenPriceGetter, +): Promise { + try { + return await tokenPriceGetter.getTokenPrice(chain); + } catch (e) { + logger.warn('Error getting token price', e); + return undefined; + } +} + +async function getNativeTokenValue( + chain: ChainName, + balanceFloat: number, + tokenPriceGetter: CoinGeckoTokenPriceGetter, +): Promise { + const price = await getTokenPriceByChain(chain, tokenPriceGetter); + logger.debug(`${chain} native token price ${price}`); + if (!price) return undefined; + return balanceFloat * price; +} + +async function getNativeTokenWarpInfo( + balance: ethers.BigNumber | bigint, + decimal: number, + tokenPriceGetter: CoinGeckoTokenPriceGetter, + chain: ChainName, +): Promise { + const balanceFloat = parseFloat(ethers.utils.formatUnits(balance, decimal)); + const value = await getNativeTokenValue( + chain, + balanceFloat, + tokenPriceGetter, + ); + return { balance: balanceFloat, valueUSD: value }; +} + +async function getCollateralTokenPrice( + tokenCoinGeckoId: string | undefined, + tokenPriceGetter: CoinGeckoTokenPriceGetter, +): Promise { + if (!tokenCoinGeckoId) return undefined; + const prices = await tokenPriceGetter.getTokenPriceByIds([tokenCoinGeckoId]); + if (!prices) return undefined; + return prices[0]; +} + +async function getCollateralTokenValue( + tokenCoinGeckoId: string | undefined, + balanceFloat: number, + tokenPriceGetter: CoinGeckoTokenPriceGetter, +): Promise { + const price = await getCollateralTokenPrice( + tokenCoinGeckoId, + tokenPriceGetter, + ); + logger.debug(`${tokenCoinGeckoId} token price ${price}`); + if (!price) return undefined; + return balanceFloat * price; +} + +async function getCollateralTokenWarpInfo( + balance: ethers.BigNumber | bigint, + decimal: number, + tokenPriceGetter: CoinGeckoTokenPriceGetter, + tokenCoinGeckoId?: string, +): Promise { + const balanceFloat = parseFloat(ethers.utils.formatUnits(balance, decimal)); + const value = await getCollateralTokenValue( + tokenCoinGeckoId, + balanceFloat, + tokenPriceGetter, + ); + return { balance: balanceFloat, valueUSD: value }; +} + async function checkWarpRouteMetrics( checkFrequency: number, tokenConfig: WarpRouteConfig, chainMetadata: ChainMap, ) { + const tokenPriceGetter = + CoinGeckoTokenPriceGetter.withDefaultCoinGecko(chainMetadata); + setInterval(async () => { try { const multiProtocolProvider = new MultiProtocolProvider(chainMetadata); - const balances = await checkBalance(tokenConfig, multiProtocolProvider); + const balances = await checkBalance( + tokenConfig, + multiProtocolProvider, + tokenPriceGetter, + ); logger.info('Token Balances:', balances); updateTokenBalanceMetrics(tokenConfig, balances); } catch (e) { logger.error('Error checking balances', e); } - try { - const xERC20Limits = await getXERC20Limits(tokenConfig, chainMetadata); - logger.info('xERC20 Limits:', xERC20Limits); - updateXERC20LimitsMetrics(xERC20Limits); - } catch (e) { - logger.error('Error checking xERC20 limits', e); + // only check xERC20 limits if there are xERC20 tokens in the config + if ( + Object.keys(tokenConfig).some( + (chain) => + tokenConfig[chain].type === TokenType.XERC20 || + tokenConfig[chain].type === TokenType.XERC20Lockbox, + ) + ) { + try { + const xERC20Limits = await getXERC20Limits(tokenConfig, chainMetadata); + logger.info('xERC20 Limits:', xERC20Limits); + updateXERC20LimitsMetrics(xERC20Limits); + } catch (e) { + logger.error('Error checking xERC20 limits', e); + } } }, checkFrequency); } diff --git a/typescript/infra/src/warp/helm.ts b/typescript/infra/src/warp/helm.ts index f5aa0e9811..bbb98a6d58 100644 --- a/typescript/infra/src/warp/helm.ts +++ b/typescript/infra/src/warp/helm.ts @@ -27,7 +27,7 @@ export class WarpRouteMonitorHelmManager extends HelmManager { return { image: { repository: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '6945b20-20241022-155935', + tag: '8e2f616-20241025-163752', }, configFilePath: pathRelativeToMonorepoRoot, fullnameOverride: this.helmReleaseName, diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index ad33e87c26..a37619fdf4 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,16 @@ # @hyperlane-xyz/sdk +## 5.6.2 + +### Patch Changes + +- 5fd4267e7: Supported non-32 byte non-EVM recipients when sending warps from Sealevel +- Updated dependencies [5fd4267e7] +- Updated dependencies [a36fc5fb2] +- Updated dependencies [a42616ff3] + - @hyperlane-xyz/utils@5.6.2 + - @hyperlane-xyz/core@5.6.1 + ## 5.6.1 ### Patch Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 44e35d35a4..133a579275 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,14 +1,14 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "5.6.1", + "version": "5.6.2", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-s3": "^3.74.0", "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/core": "5.6.0", - "@hyperlane-xyz/utils": "5.6.1", + "@hyperlane-xyz/core": "5.6.1", + "@hyperlane-xyz/utils": "5.6.2", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@safe-global/safe-deployments": "1.37.8", diff --git a/typescript/sdk/src/gas/token-prices.ts b/typescript/sdk/src/gas/token-prices.ts index 26d17cd8bc..d609496308 100644 --- a/typescript/sdk/src/gas/token-prices.ts +++ b/typescript/sdk/src/gas/token-prices.ts @@ -23,23 +23,23 @@ type TokenPriceCacheEntry = { }; class TokenPriceCache { - protected cache: Map; + protected cache: Map; protected freshSeconds: number; protected evictionSeconds: number; constructor(freshSeconds = 60, evictionSeconds = 3 * 60 * 60) { - this.cache = new Map(); + this.cache = new Map(); this.freshSeconds = freshSeconds; this.evictionSeconds = evictionSeconds; } - put(chain: ChainName, price: number): void { + put(id: string, price: number): void { const now = new Date(); - this.cache.set(chain, { timestamp: now, price }); + this.cache.set(id, { timestamp: now, price }); } - isFresh(chain: ChainName): boolean { - const entry = this.cache.get(chain); + isFresh(id: string): boolean { + const entry = this.cache.get(id); if (!entry) return false; const expiryTime = new Date( @@ -49,17 +49,17 @@ class TokenPriceCache { return now < expiryTime; } - fetch(chain: ChainName): number { - const entry = this.cache.get(chain); + fetch(id: string): number { + const entry = this.cache.get(id); if (!entry) { - throw new Error(`no entry found for ${chain} in token price cache`); + throw new Error(`no entry found for ${id} in token price cache`); } const evictionTime = new Date( entry.timestamp.getTime() + 1000 * this.evictionSeconds, ); const now = new Date(); if (now > evictionTime) { - throw new Error(`evicted entry found for ${chain} in token price cache`); + throw new Error(`evicted entry found for ${id} in token price cache`); } return entry.price; } @@ -97,20 +97,30 @@ export class CoinGeckoTokenPriceGetter implements TokenPriceGetter { ); } - async getTokenPrice(chain: ChainName): Promise { - const [price] = await this.getTokenPrices([chain]); + async getTokenPrice( + chain: ChainName, + currency: string = 'usd', + ): Promise { + const [price] = await this.getTokenPrices([chain], currency); return price; } async getTokenExchangeRate( base: ChainName, quote: ChainName, + currency: string = 'usd', ): Promise { - const [basePrice, quotePrice] = await this.getTokenPrices([base, quote]); + const [basePrice, quotePrice] = await this.getTokenPrices( + [base, quote], + currency, + ); return basePrice / quotePrice; } - private async getTokenPrices(chains: ChainName[]): Promise { + private async getTokenPrices( + chains: ChainName[], + currency: string = 'usd', + ): Promise { const isMainnet = chains.map((c) => !this.metadata[c].isTestnet); const allMainnets = isMainnet.every((v) => v === true); const allTestnets = isMainnet.every((v) => v === false); @@ -125,32 +135,43 @@ export class CoinGeckoTokenPriceGetter implements TokenPriceGetter { ); } - const toQuery = chains.filter((c) => !this.cache.isFresh(c)); + const ids = chains.map( + (chain) => this.metadata[chain].gasCurrencyCoinGeckoId || chain, + ); + + await this.getTokenPriceByIds(ids, currency); + return chains.map((chain) => + this.cache.fetch(this.metadata[chain].gasCurrencyCoinGeckoId || chain), + ); + } + + public async getTokenPriceByIds( + ids: string[], + currency: string = 'usd', + ): Promise { + const toQuery = ids.filter((id) => !this.cache.isFresh(id)); + await sleep(this.sleepMsBetweenRequests); + if (toQuery.length > 0) { + let response: any; try { - await this.queryTokenPrices(toQuery); + response = await this.coinGecko.simple.price({ + ids: toQuery, + vs_currencies: [currency], + }); + + if (response.success === true) { + const prices = toQuery.map((id) => response.data[id][currency]); + toQuery.map((id, i) => this.cache.put(id, prices[i])); + } else { + rootLogger.warn('Failed to query token prices', response.message); + return undefined; + } } catch (e) { - rootLogger.warn('Failed to query token prices', e); + rootLogger.warn('Error when querying token prices', e); + return undefined; } } - return chains.map((chain) => this.cache.fetch(chain)); - } - - private async queryTokenPrices(chains: ChainName[]): Promise { - const currency = 'usd'; - // The CoinGecko API expects, in some cases, IDs that do not match - // ChainNames. - const ids = chains.map( - (chain) => this.metadata[chain].gasCurrencyCoinGeckoId || chain, - ); - // Coingecko rate limits, so we are adding this sleep - await sleep(this.sleepMsBetweenRequests); - const response = await this.coinGecko.simple.price({ - ids, - vs_currencies: [currency], - }); - const prices = ids.map((id) => response.data[id][currency]); - // Update the cache with the newly fetched prices - chains.map((chain, i) => this.cache.put(chain, prices[i])); + return ids.map((id) => this.cache.fetch(id)); } } diff --git a/typescript/sdk/src/metadata/warpRouteConfig.ts b/typescript/sdk/src/metadata/warpRouteConfig.ts index 2fd69ce03e..8c1ee346c0 100644 --- a/typescript/sdk/src/metadata/warpRouteConfig.ts +++ b/typescript/sdk/src/metadata/warpRouteConfig.ts @@ -10,6 +10,7 @@ const TokenConfigSchema = z.object({ type: z.nativeEnum(TokenType), hypAddress: z.string(), // HypERC20Collateral, HypERC20Synthetic, HypNativeToken address tokenAddress: z.string().optional(), // external token address needed for collateral type eg tokenAddress.balanceOf(hypAddress) + tokenCoinGeckoId: z.string().optional(), // CoinGecko id for token name: z.string(), symbol: z.string(), decimals: z.number(), diff --git a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts index 8287fa07b8..1df0d86375 100644 --- a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts @@ -21,6 +21,7 @@ import { addressToBytes, eqAddress, median, + padBytesToLength, } from '@hyperlane-xyz/utils'; import { BaseSealevelAdapter } from '../../app/MultiProtocolApp.js'; @@ -295,7 +296,7 @@ export abstract class SealevelHypTokenAdapter instruction: SealevelHypTokenInstruction.TransferRemote, data: new SealevelTransferRemoteInstruction({ destination_domain: destination, - recipient: addressToBytes(recipient), + recipient: padBytesToLength(addressToBytes(recipient), 32), amount_or_id: BigInt(weiAmountOrId), }), }); diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index 8575e59fd8..bc910f9f86 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @hyperlane-xyz/utils +## 5.6.2 + +### Patch Changes + +- 5fd4267e7: Supported non-32 byte non-EVM recipients when sending warps from Sealevel +- a36fc5fb2: fix: isObject utils fn should return only boolean value + ## 5.6.1 ## 5.6.0 diff --git a/typescript/utils/package.json b/typescript/utils/package.json index e06c4cf630..6329e12034 100644 --- a/typescript/utils/package.json +++ b/typescript/utils/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/utils", "description": "General utilities and types for the Hyperlane network", - "version": "5.6.1", + "version": "5.6.2", "dependencies": { "@cosmjs/encoding": "^0.32.4", "@solana/web3.js": "^1.78.0", diff --git a/typescript/utils/src/addresses.test.ts b/typescript/utils/src/addresses.test.ts index d25e33cf8a..2313ce384c 100644 --- a/typescript/utils/src/addresses.test.ts +++ b/typescript/utils/src/addresses.test.ts @@ -4,6 +4,7 @@ import { addressToBytes, bytesToProtocolAddress, isZeroishAddress, + padBytesToLength, } from './addresses.js'; import { ProtocolType } from './types.js'; @@ -42,6 +43,17 @@ describe('Address utilities', () => { }); }); + describe('padBytesToLength', () => { + it('Pads bytes to a given length', () => { + const bytes = Buffer.from([1, 2, 3]); + expect(padBytesToLength(bytes, 5).equals(Buffer.from([0, 0, 1, 2, 3]))); + }); + it('Rejects bytes that exceed the target length', () => { + const bytes = Buffer.from([1, 2, 3]); + expect(() => padBytesToLength(bytes, 2)).to.throw(Error); + }); + }); + describe('bytesToProtocolAddress', () => { it('Converts bytes to address', () => { expect( diff --git a/typescript/utils/src/addresses.ts b/typescript/utils/src/addresses.ts index 88532464fb..01f9fdb107 100644 --- a/typescript/utils/src/addresses.ts +++ b/typescript/utils/src/addresses.ts @@ -316,6 +316,14 @@ export function bytesToBytes32(bytes: Uint8Array): string { ); } +// Pad bytes to a certain length, padding with 0s at the start +export function padBytesToLength(bytes: Uint8Array, length: number) { + if (bytes.length > length) { + throw new Error(`bytes must be ${length} bytes or less`); + } + return Buffer.concat([Buffer.alloc(length - bytes.length), bytes]); +} + export function bytesToAddressEvm(bytes: Uint8Array): Address { return bytes32ToAddress(Buffer.from(bytes).toString('hex')); } diff --git a/typescript/utils/src/index.ts b/typescript/utils/src/index.ts index 0c18543dd6..0c82c6782a 100644 --- a/typescript/utils/src/index.ts +++ b/typescript/utils/src/index.ts @@ -35,6 +35,7 @@ export { normalizeAddressCosmos, normalizeAddressEvm, normalizeAddressSealevel, + padBytesToLength, shortenAddress, strip0x, } from './addresses.js'; diff --git a/typescript/utils/src/math.test.ts b/typescript/utils/src/math.test.ts new file mode 100644 index 0000000000..e2f484d656 --- /dev/null +++ b/typescript/utils/src/math.test.ts @@ -0,0 +1,48 @@ +import { expect } from 'chai'; + +import { mean, median, randomInt, stdDev, sum } from './math.js'; + +describe('Math Utility Functions', () => { + describe('median', () => { + it('should return the median of an odd-length array', () => { + expect(median([1, 3, 2])).to.equal(2); + }); + + it('should return the median of an even-length array', () => { + expect(median([1, 2, 3, 4])).to.equal(2.5); + }); + + it('should return the median of an even-length array with non sorted numbers', () => { + expect(median([1, 2, 0, 4, 5, 6])).to.equal(3); + }); + }); + + describe('sum', () => { + it('should return the sum of an array', () => { + expect(sum([1, 2, 3, 4])).to.equal(10); + }); + }); + + describe('mean', () => { + it('should return the mean of an array', () => { + expect(mean([1, 2, 3, 4])).to.equal(2.5); + }); + }); + + describe('stdDev', () => { + it('should return the standard deviation of an array', () => { + expect(stdDev([1, 2, 3, 4])).to.be.closeTo(1.118, 0.001); + }); + }); + + describe('randomInt', () => { + it('should return a random integer within the specified range', () => { + const min = 1; + const max = 10; + const result = randomInt(max, min); + expect(result).to.be.at.least(min); + expect(result).to.be.below(max); + expect(result % 1).to.equal(0); // its an integer + }); + }); +}); diff --git a/typescript/utils/src/math.ts b/typescript/utils/src/math.ts index ebca6e75ef..4cc71bf80f 100644 --- a/typescript/utils/src/math.ts +++ b/typescript/utils/src/math.ts @@ -2,7 +2,7 @@ export function median(a: number[]): number { const sorted = a.slice().sort(); const mid = Math.floor(sorted.length / 2); const median = - sorted.length % 2 == 0 ? (sorted[mid] + sorted[mid + 1]) / 2 : sorted[mid]; + sorted.length % 2 == 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid]; return median; } diff --git a/typescript/utils/src/objects.test.ts b/typescript/utils/src/objects.test.ts index d5ed72a6be..beab06bc38 100644 --- a/typescript/utils/src/objects.test.ts +++ b/typescript/utils/src/objects.test.ts @@ -4,6 +4,7 @@ import { deepCopy, deepEquals, diffObjMerge, + isObject, objMerge, objOmit, } from './objects.js'; @@ -74,6 +75,14 @@ describe('Object utilities', () => { expect(omitted1_2).to.eql({ a: 1, b: { d: 'string' } }); }); + it('isObject', () => { + expect(isObject({})).to.be.true; + expect(isObject([])).to.be.false; + expect(isObject(null)).to.be.false; + expect(isObject(undefined)).to.be.false; + expect(isObject(42)).to.be.false; + }); + describe('diffObjMerge', () => { it('should merge objects with equal values', () => { const actual = { a: 1, b: 2 }; diff --git a/typescript/utils/src/objects.ts b/typescript/utils/src/objects.ts index 403caa849a..1917cbff69 100644 --- a/typescript/utils/src/objects.ts +++ b/typescript/utils/src/objects.ts @@ -5,8 +5,8 @@ import { ethersBigNumberSerializer } from './logging.js'; import { isNullish } from './typeof.js'; import { assert } from './validation.js'; -export function isObject(item: any) { - return item && typeof item === 'object' && !Array.isArray(item); +export function isObject(item: any): boolean { + return !!item && typeof item === 'object' && !Array.isArray(item); } export function deepEquals(v1: any, v2: any) { diff --git a/typescript/widgets/CHANGELOG.md b/typescript/widgets/CHANGELOG.md index 0ded083ed6..0371d680ca 100644 --- a/typescript/widgets/CHANGELOG.md +++ b/typescript/widgets/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/widgets +## 5.6.2 + +### Patch Changes + +- Updated dependencies [5fd4267e7] +- Updated dependencies [a36fc5fb2] + - @hyperlane-xyz/utils@5.6.2 + - @hyperlane-xyz/sdk@5.6.2 + ## 5.6.1 ### Patch Changes diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index 5160014c65..20fe43cb15 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -1,15 +1,15 @@ { "name": "@hyperlane-xyz/widgets", "description": "Common react components for Hyperlane projects", - "version": "5.6.1", + "version": "5.6.2", "peerDependencies": { "react": "^18", "react-dom": "^18" }, "dependencies": { "@headlessui/react": "^2.1.8", - "@hyperlane-xyz/sdk": "5.6.1", - "@hyperlane-xyz/utils": "5.6.1", + "@hyperlane-xyz/sdk": "5.6.2", + "@hyperlane-xyz/utils": "5.6.2", "clsx": "^2.1.1", "react-tooltip": "^5.28.0" }, diff --git a/yarn.lock b/yarn.lock index 279fe75e60..ecd030eab6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7824,8 +7824,8 @@ __metadata: "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" "@hyperlane-xyz/registry": "npm:4.7.0" - "@hyperlane-xyz/sdk": "npm:5.6.1" - "@hyperlane-xyz/utils": "npm:5.6.1" + "@hyperlane-xyz/sdk": "npm:5.6.2" + "@hyperlane-xyz/utils": "npm:5.6.2" "@inquirer/core": "npm:9.0.10" "@inquirer/figures": "npm:1.0.5" "@inquirer/prompts": "npm:^3.0.0" @@ -7860,13 +7860,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:5.6.0, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:5.6.1, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@arbitrum/nitro-contracts": "npm:^1.2.1" "@eth-optimism/contracts": "npm:^0.6.0" - "@hyperlane-xyz/utils": "npm:5.6.1" + "@hyperlane-xyz/utils": "npm:5.6.2" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" @@ -7917,13 +7917,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@npm:5.6.1, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:5.6.2, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: - "@hyperlane-xyz/core": "npm:5.6.0" + "@hyperlane-xyz/core": "npm:5.6.1" "@hyperlane-xyz/registry": "npm:4.7.0" - "@hyperlane-xyz/sdk": "npm:5.6.1" + "@hyperlane-xyz/sdk": "npm:5.6.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -7970,10 +7970,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.2" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:5.6.1" - "@hyperlane-xyz/registry": "npm:4.7.0" - "@hyperlane-xyz/sdk": "npm:5.6.1" - "@hyperlane-xyz/utils": "npm:5.6.1" + "@hyperlane-xyz/helloworld": "npm:5.6.2" + "@hyperlane-xyz/registry": "npm:4.10.0" + "@hyperlane-xyz/sdk": "npm:5.6.2" + "@hyperlane-xyz/utils": "npm:5.6.2" "@inquirer/prompts": "npm:^5.3.8" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" @@ -8029,6 +8029,16 @@ __metadata: languageName: unknown linkType: soft +"@hyperlane-xyz/registry@npm:4.10.0": + version: 4.10.0 + resolution: "@hyperlane-xyz/registry@npm:4.10.0" + dependencies: + yaml: "npm:2.4.5" + zod: "npm:^3.21.2" + checksum: 22bb18f426cbada8b97db0894fe5d0980dfc08ecbd5174c978b7aeb6d8df9706f93d7e9cf0630644d2455ad05feee714dc2a38ec515a717b0b257184637902fb + languageName: node + linkType: hard + "@hyperlane-xyz/registry@npm:4.7.0": version: 4.7.0 resolution: "@hyperlane-xyz/registry@npm:4.7.0" @@ -8039,7 +8049,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:5.6.1, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:5.6.2, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: @@ -8047,8 +8057,8 @@ __metadata: "@aws-sdk/client-s3": "npm:^3.74.0" "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" "@cosmjs/stargate": "npm:^0.32.4" - "@hyperlane-xyz/core": "npm:5.6.0" - "@hyperlane-xyz/utils": "npm:5.6.1" + "@hyperlane-xyz/core": "npm:5.6.1" + "@hyperlane-xyz/utils": "npm:5.6.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -8089,7 +8099,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@npm:5.6.1, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:5.6.2, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: @@ -8115,8 +8125,8 @@ __metadata: dependencies: "@headlessui/react": "npm:^2.1.8" "@hyperlane-xyz/registry": "npm:4.7.0" - "@hyperlane-xyz/sdk": "npm:5.6.1" - "@hyperlane-xyz/utils": "npm:5.6.1" + "@hyperlane-xyz/sdk": "npm:5.6.2" + "@hyperlane-xyz/utils": "npm:5.6.2" "@storybook/addon-essentials": "npm:^7.6.14" "@storybook/addon-interactions": "npm:^7.6.14" "@storybook/addon-links": "npm:^7.6.14"