diff --git a/CHANGELOG.md b/CHANGELOG.md index c014ed50c..627a7607f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,19 +10,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Each version will have a separate `Breaking Changes` section as well. To describe in how to upgrade from one version to another if needed ## [Unreleased] + ### Added + +* feat: Added Telemetry with Posthog . This is implemented in the sub-crate `eigen-telemetry`. + * To enable and use telemetry in your avs/code, you need to include the SDK crates with the feature telemetry enabled. In the main function the code must set the config of the telemetry with the given parameters: + + ```rust + fn main() { + let _ = eigen_telemetry::telemetry::Telemetry::set_config("YOUR_TELEMETRY_KEY", "YOUR_USER_ID"); + // ... + } + ``` + ### Changed + * Changes in the way bindings are generated in [#245](https://github.com/Layr-Labs/eigensdk-rs/pull/243). * The `bindings` target now generates the bindings using Docker with Foundry v0.3.0. * The previous `bindings` target was renamed to `bindings_host`, as it runs without Docker. However the `bindings_host` target is for CI use only. To generate the bindings, please use the `bindings` target. - ### Breaking changes + ### Removed ## [0.1.3] - 2024-01-17 + ### Added 🎉 -* feat: add rewards-v2 related functionality by @supernovahs in https://github.com/Layr-Labs/eigensdk-rs/pull/221 + +* feat: add rewards-v2 related functionality by @supernovahs in * New methods in `ELChainReader`: * `get_operator_avs_split` * `get_operator_pi_split` @@ -32,26 +47,32 @@ Each version will have a separate `Breaking Changes` section as well. To describ * Bindings updated for rewards-v2 core contracts release ### Breaking Changes 🛠 -* feat!: remove delegation manager from `ELChainWriter` by @supernovahs in https://github.com/Layr-Labs/eigensdk-rs/pull/214 + +* feat!: remove delegation manager from `ELChainWriter` by @supernovahs in * `ELChainWriter::new` no longer receives the delegation manager address as first parameter. -* feat!: change way bindings are generated by @MegaRedHand in https://github.com/Layr-Labs/eigensdk-rs/pull/204 +* feat!: change way bindings are generated by @MegaRedHand in * `eigen_utils::core` contains bindings related to core contracts * `eigen_utils::middleware` contains bindings related to middleware contracts * `eigen_utils::sdk` contains bindings related to the SDK (should only be used for testing) ### Documentation 📚 -* docs: add CHANGELOG.md by @lferrigno in https://github.com/Layr-Labs/eigensdk-rs/pull/220 + +* docs: add CHANGELOG.md by @lferrigno in + ### Other Changes -* ci: change docker setup action for official one by @MegaRedHand in https://github.com/Layr-Labs/eigensdk-rs/pull/219 -* docs: add error message for `cargo test` on darwin by @MegaRedHand in https://github.com/Layr-Labs/eigensdk-rs/pull/215 -* test: fix `test_register_and_update_operator` by @ricomateo in https://github.com/Layr-Labs/eigensdk-rs/pull/223 -* chore: update way anvil state dump is generated by @ricomateo in https://github.com/Layr-Labs/eigensdk-rs/pull/222 -* fix: disable doctests on `eigen-utils` by @MegaRedHand in https://github.com/Layr-Labs/eigensdk-rs/pull/227 -* chore: bump version by @MegaRedHand in https://github.com/Layr-Labs/eigensdk-rs/pull/228 -* docs: add GitHub release changelog configuration by @MegaRedHand in https://github.com/Layr-Labs/eigensdk-rs/pull/229 + +* ci: change docker setup action for official one by @MegaRedHand in +* docs: add error message for `cargo test` on darwin by @MegaRedHand in +* test: fix `test_register_and_update_operator` by @ricomateo in +* chore: update way anvil state dump is generated by @ricomateo in +* fix: disable doctests on `eigen-utils` by @MegaRedHand in +* chore: bump version by @MegaRedHand in +* docs: add GitHub release changelog configuration by @MegaRedHand in ## [0.1.2] - 2025-01-09 + ### Added + * Added retries with exponential backoff to send transactions in [#158](https://github.com/Layr-Labs/eigensdk-rs/pull/158) * Added `query_registration_detail` method in [#162](https://github.com/Layr-Labs/eigensdk-rs/pull/162) * Added clippy lints in `Cargo.toml` in [#159](https://github.com/Layr-Labs/eigensdk-rs/pull/159) @@ -59,16 +80,19 @@ Each version will have a separate `Breaking Changes` section as well. To describ * Added `common` crate to `eigensdk` crate in [#213](https://github.com/Layr-Labs/eigensdk-rs/pull/213) ### Changed + * Updated `eigenlayer-middleware` to v0.4.3 (rewards release) in [#177](https://github.com/Layr-Labs/eigensdk-rs/pull/177) * Fixed Holesky RPC provider URL in [#184](https://github.com/Layr-Labs/eigensdk-rs/pull/184) * Fixed BLS signature logic in [#174](https://github.com/Layr-Labs/eigensdk-rs/pull/174) ### Removed + * Deleted `TxManager` in [#151](https://github.com/Layr-Labs/eigensdk-rs/pull/151) * Removed `TxManager` crate import in [#211](https://github.com/Layr-Labs/eigensdk-rs/pull/211) * Removed logs in `operatorsinfo` test in [#185](https://github.com/Layr-Labs/eigensdk-rs/pull/185) ### Documentation + * Added notes for running tests in [#194](https://github.com/Layr-Labs/eigensdk-rs/pull/194) * Added "Contract Bindings" section to the README in [#178](https://github.com/Layr-Labs/eigensdk-rs/pull/178) * Added "Branches" section to the README in [#200](https://github.com/Layr-Labs/eigensdk-rs/pull/200) diff --git a/Cargo.lock b/Cargo.lock index 9b670ee24..cb2c1be1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2509,6 +2509,7 @@ dependencies = [ "eigen-common", "eigen-crypto-bls", "eigen-logging", + "eigen-telemetry", "eigen-testing-utils", "eigen-types", "eigen-utils", @@ -2527,6 +2528,7 @@ dependencies = [ "alloy", "eigen-common", "eigen-logging", + "eigen-telemetry", "eigen-testing-utils", "eigen-types", "eigen-utils", @@ -2773,6 +2775,13 @@ dependencies = [ "url", ] +[[package]] +name = "eigen-telemetry" +version = "0.1.3" +dependencies = [ + "posthog-rs", +] + [[package]] name = "eigen-testing-utils" version = "0.1.3" @@ -5478,6 +5487,18 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +[[package]] +name = "posthog-rs" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad1b35ffe50419992615288c40ee90fbf30da4c6faf251414675ea64e1cdfa3" +dependencies = [ + "chrono", + "reqwest 0.11.27", + "serde", + "serde_json", +] + [[package]] name = "powerfmt" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index c345bb044..55c59c1e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ clippy.question_mark = "warn" clippy.implicit_return = "allow" [workspace.dependencies] +#misc eigen-client-avsregistry = { path = "crates/chainio/clients/avsregistry" } ark-bn254 = "0.5.0" ark-ec = "0.5.0" @@ -81,6 +82,7 @@ eigen-services-blsaggregation = { path = "crates/services/bls_aggregation" } eigen-services-operatorsinfo = { path = "crates/services/operatorsinfo" } eigen-signer = { path = "crates/signer/" } eigen-testing-utils = { path = "testing/testing-utils" } +eigen-telemetry = { path = "crates/telemetry/" } eigen-types = { path = "crates/types/" } eigen-utils = { path = "crates/utils/" } eigen-nodeapi = { path = "crates/nodeapi/" } @@ -116,13 +118,8 @@ tokio = { version = "1.41", features = ["test-util", "full", "sync"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["json"] } url = "2.5" - -#misc rust-bls-bn254 = { version = "0.2.1", features = ["std"] } uuid = { version = "1.11", features = ["v4"] } - - -#misc parking_lot = "0.12" @@ -140,3 +137,5 @@ alloy = { version = "0.9", features = [ avsregistry-read = { path = "examples/avsregistry-read" } avsregistry-write = { path = "examples/avsregistry-write" } + +posthog-rs = "0.2.0" diff --git a/crates/chainio/clients/avsregistry/Cargo.toml b/crates/chainio/clients/avsregistry/Cargo.toml index 757a74bfc..2c48c3a1c 100644 --- a/crates/chainio/clients/avsregistry/Cargo.toml +++ b/crates/chainio/clients/avsregistry/Cargo.toml @@ -10,16 +10,18 @@ license-file.workspace = true [dependencies] alloy.workspace = true +ark-ff.workspace = true async-trait.workspace = true -num-bigint = "0.4.4" eigen-types.workspace = true eigen-crypto-bls.workspace = true -ark-ff.workspace = true +eigen-telemetry = { workspace = true, optional = true } eigen-client-elcontracts.workspace = true eigen-common.workspace = true eigen-utils.workspace = true eigen-logging.workspace = true +num-bigint = "0.4.4" thiserror.workspace = true +tokio = { workspace = true, optional = true } tracing.workspace = true [lints] @@ -30,3 +32,6 @@ eigen-testing-utils.workspace = true hex = "0.4.3" once_cell.workspace = true tokio = { version = "1.37.0", features = ["test-util", "full", "sync"] } + +[features] +telemetry = ["dep:eigen-telemetry", "dep:tokio"] diff --git a/crates/chainio/clients/avsregistry/src/error.rs b/crates/chainio/clients/avsregistry/src/error.rs index 69ffb3933..96ccd1c17 100644 --- a/crates/chainio/clients/avsregistry/src/error.rs +++ b/crates/chainio/clients/avsregistry/src/error.rs @@ -157,9 +157,14 @@ pub enum AvsRegistryError { /// Invalid Signature #[error("Invalid signature")] InvalidSignature, + /// Parse BigInt #[error("big int error")] ParseBigIntError, + + /// Telemetry error + #[error("Telemetry error")] + TelemetryError(String), } impl From for AvsRegistryError { diff --git a/crates/chainio/clients/avsregistry/src/reader.rs b/crates/chainio/clients/avsregistry/src/reader.rs index dbda2ca38..bf5ecfb58 100644 --- a/crates/chainio/clients/avsregistry/src/reader.rs +++ b/crates/chainio/clients/avsregistry/src/reader.rs @@ -105,6 +105,17 @@ impl AvsRegistryReader for AvsRegistryChainReader { .map_err(|_| AvsRegistryError::GetOperatorState)?; let OperatorStateRetriever::getOperatorState_0Return { _0: quorum } = operator_state; + + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "get_operators_stake_in_quorums_at_block", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } Ok(quorum) } @@ -114,6 +125,17 @@ impl AvsRegistryReader for AvsRegistryChainReader { quorum_numbers: Vec, non_signer_operator_ids: Vec>, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.get_check_signatures_indices", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_operator_state_retriever = @@ -137,6 +159,17 @@ impl AvsRegistryReader for AvsRegistryChainReader { &self, operator_id: [u8; 32], ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.get_operator_from_id", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_registry_coordinator = @@ -169,6 +202,16 @@ impl AvsRegistryChainReader { operator_state_retriever_addr: Address, http_provider_url: String, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = + eigen_telemetry::telemetry::Telemetry::capture_event("avsregistryreader.new") + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&http_provider_url); let contract_registry_coordinator = @@ -209,6 +252,17 @@ impl AvsRegistryChainReader { /// /// The total quorum count read from the RegistryCoordinator. pub async fn get_quorum_count(&self) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.get_quorum_count", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_registry_coordinator = @@ -241,6 +295,17 @@ impl AvsRegistryChainReader { block_number: u32, operator_id: B256, ) -> Result<(U256, Vec>), AvsRegistryError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.get_operators_stake_in_quorums_at_block_operator_id", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_operator_state_retriever = @@ -276,6 +341,17 @@ impl AvsRegistryChainReader { &self, quorum_numbers: Bytes, ) -> Result>, AvsRegistryError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.get_operators_stake_in_quorums_at_current_block", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let current_block_number = provider @@ -309,6 +385,17 @@ impl AvsRegistryChainReader { operator_id: B256, block_number: u32, ) -> Result<(Vec, Vec>), AvsRegistryError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.get_operators_stake_in_quorums_of_operator_at_block", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let (quorum_bitmaps, operator_stakes) = self .get_operators_stake_in_quorums_at_block_operator_id(block_number, operator_id) .await @@ -334,6 +421,17 @@ impl AvsRegistryChainReader { &self, operator_id: B256, ) -> Result<(Vec, Vec>), AvsRegistryError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.get_operators_stake_in_quorums_of_operator_at_current_block", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let current_block_number = provider.get_block_number().await.map_err(|e| { @@ -365,6 +463,17 @@ impl AvsRegistryChainReader { &self, operator_id: B256, ) -> Result, AvsRegistryError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.get_operator_stake_in_quorums_of_operator_at_current_block", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let registry_coordinator = @@ -449,6 +558,17 @@ impl AvsRegistryChainReader { &self, operator_address: Address, ) -> Result, AvsRegistryError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.get_operator_id", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_registry_coordinator = @@ -475,6 +595,17 @@ impl AvsRegistryChainReader { &self, operator_address: Address, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.is_operator_registered", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_registry_coordinator = @@ -510,6 +641,17 @@ impl AvsRegistryChainReader { mut stop_block: u64, ws_url: String, ) -> Result<(Vec
, Vec), AvsRegistryError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.query_existing_registered_operator_pub_keys", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_ws_provider(&ws_url).await.map_err(|e| { AvsRegistryError::AlloyContractError(alloy::contract::Error::TransportError(e)) })?; @@ -582,6 +724,17 @@ impl AvsRegistryChainReader { start_block: u64, stop_block: u64, ) -> Result, String>, AvsRegistryError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistryreader.query_existing_registered_operator_sockets", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let mut operator_id_to_socket = HashMap::new(); diff --git a/crates/chainio/clients/avsregistry/src/writer.rs b/crates/chainio/clients/avsregistry/src/writer.rs index 9355e8ae3..7ff74462b 100644 --- a/crates/chainio/clients/avsregistry/src/writer.rs +++ b/crates/chainio/clients/avsregistry/src/writer.rs @@ -61,6 +61,17 @@ impl AvsRegistryChainWriter { registry_coordinator_addr: Address, operator_state_retriever_addr: Address, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistrychainwriter.build_avs_registry_chain_writer", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let fill_provider = get_provider(&provider); let contract_registry_coordinator = @@ -143,6 +154,17 @@ impl AvsRegistryChainWriter { quorum_numbers: Bytes, socket: String, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistrychainwriter.register_operator_in_quorum_with_avs_registry_coordinator", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_signer(&self.signer.clone(), &self.provider); let wallet = PrivateKeySigner::from_str(&self.signer) .map_err(|_| AvsRegistryError::InvalidPrivateKey)?; @@ -223,6 +245,7 @@ impl AvsRegistryChainWriter { .map_err(AvsRegistryError::AlloyContractError)?; info!(tx_hash = ?tx,"Sent transaction to register operator in the AVS's registry coordinator" ); + Ok(*tx.tx_hash()) } @@ -247,6 +270,17 @@ impl AvsRegistryChainWriter { operators_per_quorum: Vec>, quorum_number: Bytes, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistrychainwriter.update_stakes_of_entire_operator_set_for_quorums", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + info!(quorum_numbers = %quorum_number, "updating stakes for entire operator set"); let provider = get_signer(&self.signer.clone(), &self.provider); let contract_registry_coordinator = @@ -278,6 +312,17 @@ impl AvsRegistryChainWriter { &self, operators: Vec
, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistrychainwriter.update_stakes_of_operator_subset_for_all_quorums", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + info!(operators = ?operators, "updating stakes of operator subset for all quorums"); let provider = get_signer(&self.signer.clone(), &self.provider); @@ -311,6 +356,17 @@ impl AvsRegistryChainWriter { &self, quorum_numbers: Bytes, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "avsregistrychainwriter.deregister_operator", + ) + .map_err(|e| AvsRegistryError::TelemetryError(e.to_string())); + }) + .await; + } + info!("deregistering operator with the AVS's registry coordinator"); let provider = get_signer(&self.signer.clone(), &self.provider); diff --git a/crates/chainio/clients/elcontracts/Cargo.toml b/crates/chainio/clients/elcontracts/Cargo.toml index 641229a11..57779baf4 100644 --- a/crates/chainio/clients/elcontracts/Cargo.toml +++ b/crates/chainio/clients/elcontracts/Cargo.toml @@ -12,9 +12,11 @@ license-file.workspace = true alloy.workspace = true eigen-common.workspace = true eigen-logging.workspace = true +eigen-telemetry = { workspace = true, optional = true } eigen-types.workspace = true eigen-utils.workspace = true thiserror.workspace = true +tokio = { workspace = true, optional = true } tracing.workspace = true [dev-dependencies] @@ -22,3 +24,6 @@ eigen-testing-utils.workspace = true once_cell.workspace = true serial_test.workspace = true tokio.workspace = true + +[features] +telemetry = ["dep:eigen-telemetry", "dep:tokio"] diff --git a/crates/chainio/clients/elcontracts/src/error.rs b/crates/chainio/clients/elcontracts/src/error.rs index 5158c6bf7..8c0073dbe 100644 --- a/crates/chainio/clients/elcontracts/src/error.rs +++ b/crates/chainio/clients/elcontracts/src/error.rs @@ -72,4 +72,8 @@ pub enum ElContractsError { #[error("Alloy pending Transaction error {0}")] AlloyPendingTransactionError(#[from] PendingTransactionError), + + /// Telemetry error + #[error("Telemetry error")] + TelemetryError(String), } diff --git a/crates/chainio/clients/elcontracts/src/reader.rs b/crates/chainio/clients/elcontracts/src/reader.rs index 678b60e90..2b74de5f7 100644 --- a/crates/chainio/clients/elcontracts/src/reader.rs +++ b/crates/chainio/clients/elcontracts/src/reader.rs @@ -31,13 +31,22 @@ impl ELChainReader { /// # Returns /// /// A new `ELChainReader` instance. - pub fn new( + pub async fn new( _logger: SharedLogger, slasher: Address, delegation_manager: Address, avs_directory: Address, provider: String, ) -> Self { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event("elchainreader.new") + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + ELChainReader { _logger, slasher, @@ -68,6 +77,15 @@ impl ELChainReader { avs_directory: Address, client: &String, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event("elchainreader.build") + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(client); let contract_delegation_manager = DelegationManager::new(delegation_manager, provider); @@ -114,6 +132,17 @@ impl ELChainReader { approve_salt: FixedBytes<32>, expiry: U256, ) -> Result, ElContractsError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainreader.calculate_delegation_approval_digest_hash", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_delegation_manager = DelegationManager::new(self.delegation_manager, provider); let delegation_approval_digest_hash = contract_delegation_manager @@ -157,6 +186,17 @@ impl ELChainReader { salt: FixedBytes<32>, expiry: U256, ) -> Result, ElContractsError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainreader.calculate_operator_avs_registration_digest_hash", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_avs_directory = AVSDirectory::new(self.avs_directory, provider); @@ -192,6 +232,17 @@ impl ELChainReader { operator_addr: Address, strategy_addr: Address, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainreader.get_operator_shares_in_strategy", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_delegation_manager = DelegationManager::new(self.delegation_manager, provider); @@ -221,6 +272,17 @@ impl ELChainReader { &self, operator_addr: Address, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainreader.operator_is_frozen", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_slasher = ISlasher::new(self.slasher, provider); @@ -254,6 +316,17 @@ impl ELChainReader { operator_addr: Address, service_manager_addr: Address, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainreader.service_manager_can_slash_operator_until_block", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_slasher = ISlasher::new(self.slasher, provider); @@ -289,6 +362,17 @@ impl ELChainReader { &self, strategy_addr: Address, ) -> Result<(Address, Address, Address), ElContractsError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainreader.get_strategy_and_underlying_erc20_token", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_strategy = IStrategy::new(strategy_addr, &provider); @@ -329,6 +413,17 @@ impl ELChainReader { &self, operator: Address, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainreader.get_operator_details", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_delegation_manager = @@ -370,6 +465,17 @@ impl ELChainReader { &self, operator: Address, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainreader.is_operator_registered", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_provider(&self.provider); let contract_delegation_manager = DelegationManager::new(self.delegation_manager, provider); @@ -498,6 +604,7 @@ mod tests { avs_directory_address, http_endpoint, ) + .await } #[tokio::test] diff --git a/crates/chainio/clients/elcontracts/src/writer.rs b/crates/chainio/clients/elcontracts/src/writer.rs index 1618ecd13..9227b4b83 100644 --- a/crates/chainio/clients/elcontracts/src/writer.rs +++ b/crates/chainio/clients/elcontracts/src/writer.rs @@ -36,6 +36,14 @@ impl ELChainWriter { provider: String, signer: String, ) -> Self { + #[cfg(feature = "telemetry")] + { + tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event("elchainwriter.new") + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }); + } + Self { strategy_manager, rewards_coordinator, @@ -62,6 +70,17 @@ impl ELChainWriter { &self, operator: Operator, ) -> Result, ElContractsError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainwriter.register_as_operator", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + info!("registering operator {:?} to EigenLayer", operator.address); let op_details = OperatorDetails { __deprecated_earningsReceiver: operator.earnings_receiver_address, @@ -116,6 +135,17 @@ impl ELChainWriter { &self, operator: Operator, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainwriter.update_operator_details", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + info!( "updating operator detils of operator {:?} to EigenLayer", operator.address @@ -167,6 +197,17 @@ impl ELChainWriter { strategy_addr: Address, amount: U256, ) -> Result { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainwriter.deposit_erc20_into_strategy", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + info!("depositing {amount:?} tokens into strategy {strategy_addr:?}"); let tokens = self .el_chain_reader @@ -209,6 +250,17 @@ impl ELChainWriter { &self, claimer: Address, ) -> Result, ElContractsError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainwriter.set_claimer_for", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_signer(&self.signer, &self.provider); let contract_rewards_coordinator = @@ -240,6 +292,17 @@ impl ELChainWriter { earner_address: Address, claim: RewardsMerkleClaim, ) -> Result, ElContractsError> { + #[cfg(feature = "telemetry")] + { + let _ = tokio::task::spawn_blocking(move || { + let _ = eigen_telemetry::telemetry::Telemetry::capture_event( + "elchainwriter.process_claim", + ) + .map_err(|e| ElContractsError::TelemetryError(e.to_string())); + }) + .await; + } + let provider = get_signer(&self.signer, &self.provider); let contract_rewards_coordinator = @@ -406,7 +469,8 @@ mod tests { delegation_manager_address, avs_directory_address, http_endpoint, - ), + ) + .await, delegation_manager_address, ) } diff --git a/crates/services/operatorsinfo/src/operatorsinfo_inmemory.rs b/crates/services/operatorsinfo/src/operatorsinfo_inmemory.rs index d2fcccb33..d32a89e27 100644 --- a/crates/services/operatorsinfo/src/operatorsinfo_inmemory.rs +++ b/crates/services/operatorsinfo/src/operatorsinfo_inmemory.rs @@ -648,7 +648,8 @@ mod tests { delegation_manager_address, avs_directory_address, http_endpoint.to_string(), - ); + ) + .await; let signer = PrivateKeySigner::from_str(pvt_key).unwrap(); let el_chain_writer = ELChainWriter::new( diff --git a/crates/telemetry/Cargo.toml b/crates/telemetry/Cargo.toml new file mode 100644 index 000000000..4111b6b03 --- /dev/null +++ b/crates/telemetry/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "eigen-telemetry" +description = "Eigen Layer telemetry" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license-file.workspace = true + +[dependencies] +posthog-rs.workspace = true diff --git a/crates/telemetry/README.md b/crates/telemetry/README.md new file mode 100644 index 000000000..396512d6d --- /dev/null +++ b/crates/telemetry/README.md @@ -0,0 +1,18 @@ +# Eigen-telemetry + +To enable and use telemetry in your avs/code, you need to include `eigen-telemetry` crate and the rest of the SDK crates with the feature telemetry enabled. To do so, in your `Cargo.toml` file put: + +``` toml +eigen-telemetry = "*" +eigen-client-avsregistry = { version = "*", features = "telemetry" } +``` + +In the main function the code must set the config of the telemetry with the given parameters: + +``` rust +fn main() { + let _ = eigen_telemetry::telemetry::Telemetry::set_config("YOUR_TELEMETRY_KEY", "YOUR_USER_ID"); + + // ... the rest of the code ... +} +``` diff --git a/crates/telemetry/src/lib.rs b/crates/telemetry/src/lib.rs new file mode 100644 index 000000000..96bb08ec0 --- /dev/null +++ b/crates/telemetry/src/lib.rs @@ -0,0 +1,7 @@ +pub mod telemetry; +pub mod telemetry_config; + +use std::sync::OnceLock; +use telemetry_config::TelemetryConfig; + +static TELEMETRY_CELL: OnceLock = OnceLock::new(); diff --git a/crates/telemetry/src/telemetry.rs b/crates/telemetry/src/telemetry.rs new file mode 100644 index 000000000..ed4d8ee66 --- /dev/null +++ b/crates/telemetry/src/telemetry.rs @@ -0,0 +1,18 @@ +use super::telemetry_config::TelemetryConfig; +use posthog_rs::{Error, Event}; + +pub struct Telemetry {} + +impl Telemetry { + pub fn set_config(key: &str, user_id: &str) -> Result<(), TelemetryConfig> { + let config = TelemetryConfig::new(key, user_id); + crate::TELEMETRY_CELL.set(config)?; + Ok(()) + } + + pub fn capture_event(event: &str) -> Result<(), Error> { + let cell = crate::TELEMETRY_CELL.get().unwrap(); + let event = Event::new(event, &cell.user_id); + cell.client.capture(event) + } +} diff --git a/crates/telemetry/src/telemetry_config.rs b/crates/telemetry/src/telemetry_config.rs new file mode 100644 index 000000000..952169873 --- /dev/null +++ b/crates/telemetry/src/telemetry_config.rs @@ -0,0 +1,19 @@ +use posthog_rs::{client, Client}; + +pub struct TelemetryConfig { + pub key: String, + pub user_id: String, + pub(crate) client: Client, +} + +impl TelemetryConfig { + pub(crate) fn new(key: &str, user_id: &str) -> Self { + let client = client(key); + + Self { + key: key.to_owned(), + user_id: user_id.to_owned(), + client, + } + } +} diff --git a/examples/avsregistry-write/examples/register_operator_in_quorum_with_avs_registry_coordinator.rs b/examples/avsregistry-write/examples/register_operator_in_quorum_with_avs_registry_coordinator.rs index cb25e3e5c..bfd9f2099 100644 --- a/examples/avsregistry-write/examples/register_operator_in_quorum_with_avs_registry_coordinator.rs +++ b/examples/avsregistry-write/examples/register_operator_in_quorum_with_avs_registry_coordinator.rs @@ -66,7 +66,8 @@ async fn main() -> Result<()> { DELEGATION_MANAGER_ADDRESS, AVS_DIRECTORY_ADDRESS, "https://ethereum-holesky.blockpi.network/v1/rpc/public".to_string(), - ); + ) + .await; // A new ElChainWriter instance let el_writer = ELChainWriter::new( STRATEGY_MANAGER_ADDRESS, diff --git a/examples/info-operator-service/examples/get_operator_info.rs b/examples/info-operator-service/examples/get_operator_info.rs index 19cdedddb..95495d8e6 100644 --- a/examples/info-operator-service/examples/get_operator_info.rs +++ b/examples/info-operator-service/examples/get_operator_info.rs @@ -85,7 +85,8 @@ pub async fn register_operator(pvt_key: &str, bls_key: &str, http_endpoint: &str delegation_manager_address, avs_directory_address, http_endpoint.to_owned(), - ); + ) + .await; let el_chain_writer = ELChainWriter::new( strategy_manager_address,