From ed035c3c9a800190a1d4e4cf688ed8f3c9021699 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 5 Dec 2024 17:47:47 -0700 Subject: [PATCH 01/33] everything innit --- .../main/chains/hyperlane-sealevel/src/lib.rs | 1 + .../chains/hyperlane-sealevel/src/mailbox.rs | 334 ++++++++++++------ .../hyperlane-sealevel/src/priority_fee.rs | 185 ++++++++++ .../hyperlane-sealevel/src/rpc/client.rs | 22 +- .../hyperlane-sealevel/src/trait_builder.rs | 59 ++++ .../src/settings/parser/connection_parser.rs | 63 ++++ 6 files changed, 538 insertions(+), 126 deletions(-) create mode 100644 rust/main/chains/hyperlane-sealevel/src/priority_fee.rs diff --git a/rust/main/chains/hyperlane-sealevel/src/lib.rs b/rust/main/chains/hyperlane-sealevel/src/lib.rs index 90a2e01b66..05db6a7d93 100644 --- a/rust/main/chains/hyperlane-sealevel/src/lib.rs +++ b/rust/main/chains/hyperlane-sealevel/src/lib.rs @@ -23,6 +23,7 @@ mod log_meta_composer; mod mailbox; mod merkle_tree_hook; mod multisig_ism; +mod priority_fee; mod provider; mod rpc; mod trait_builder; diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index d64963e242..ba15cff1c7 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -61,13 +61,17 @@ use hyperlane_core::{ TxCostEstimate, TxOutcome, H256, H512, U256, }; -use crate::account::{search_accounts_by_discriminator, search_and_validate_account}; use crate::error::HyperlaneSealevelError; use crate::log_meta_composer::{ is_interchain_payment_instruction, is_message_delivery_instruction, is_message_dispatch_instruction, LogMetaComposer, }; use crate::utils::{decode_h256, decode_h512, from_base58}; +use crate::{ + account::{search_accounts_by_discriminator, search_and_validate_account}, + priority_fee::PriorityFeeOracle, + PriorityFeeOracleConfig, +}; use crate::{ConnectionConf, SealevelProvider, SealevelRpcClient}; const SYSTEM_PROGRAM: &str = "11111111111111111111111111111111"; @@ -77,6 +81,9 @@ const SPL_NOOP: &str = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"; // TODO: consider a more sane value and/or use IGP gas payments instead. const PROCESS_COMPUTE_UNITS: u32 = 1_400_000; +/// The max amount of compute units for a transaction. +const MAX_COMPUTE_UNITS: u32 = 1_400_000; + /// 0.0005 SOL, in lamports. /// A typical tx fee without a prioritization fee is 0.000005 SOL, or /// 5000 lamports. (Example: https://explorer.solana.com/tx/fNd3xVeBzFHeuzr8dXQxLGiHMzTeYpykSV25xWzNRaHtzzjvY9A3MzXh1ZsK2JncRHkwtuWrGEwGXVhFaUCYhtx) @@ -102,6 +109,7 @@ pub struct SealevelMailbox { pub(crate) outbox: (Pubkey, u8), pub(crate) provider: SealevelProvider, payer: Option, + priority_fee_oracle: Box, } impl SealevelMailbox { @@ -128,6 +136,7 @@ impl SealevelMailbox { outbox, provider, payer, + priority_fee_oracle: conf.priority_fee_oracle.create_oracle(), }) } @@ -291,6 +300,202 @@ impl SealevelMailbox { ) } + /// Builds a transaction with estimated costs for a given instruction. + async fn build_estimated_tx_for_instruction( + &self, + instruction: Instruction, + ) -> ChainResult { + // Build a transaction that sets the max compute units and a dummy compute unit price. + // This is used for simulation to get the actual compute unit limit. We set dummy values + // for the compute unit limit and price because we want to include the instructions that + // set these in the cost estimate. + let simulation_tx = self + .create_transaction_for_instruction( + MAX_COMPUTE_UNITS, + 0, + Some(Hash::default()), + instruction.clone(), + ) + .await?; + + let simulation_result = self + .provider + .rpc() + .simulate_transaction(&simulation_tx) + .await?; + let simulation_compute_units: u32 = simulation_result + .units_consumed + .ok_or_else(|| { + ChainCommunicationError::from_other_str( + "No compute units used in simulation result", + ) + })? + .try_into() + .map_err(ChainCommunicationError::from_other)?; + // Bump the compute units by 10% to ensure we have enough, but cap it at the max. + let simulation_compute_units = MAX_COMPUTE_UNITS.min((simulation_compute_units * 11) / 10); + + let priority_fee = self + .priority_fee_oracle + .get_priority_fee(&simulation_tx) + .await?; + + // Build the final transaction with the correct compute unit limit and price. + let tx = self + .create_transaction_for_instruction( + simulation_compute_units, + priority_fee, + None, + instruction, + ) + .await?; + + Ok(tx) + } + + /// Creates a transaction for a given instruction, compute unit limit, and compute unit price. + /// If `blockhash` is `None`, the latest blockhash is fetched from the RPC. + async fn create_transaction_for_instruction( + &self, + compute_unit_limit: u32, + compute_unit_price_micro_lamports: u64, + blockhash: Option, + instruction: Instruction, + ) -> ChainResult { + let payer = self.get_payer()?; + + let mut instructions = Vec::with_capacity(3); + // Set the compute unit limit. + instructions.push(ComputeBudgetInstruction::set_compute_unit_limit( + compute_unit_limit, + )); + + // If we're using Jito, we need to send a tip to the Jito fee account. + // Otherwise, we need to set the compute unit price. + if self.use_jito() { + let tip: u64 = compute_unit_price_micro_lamports * compute_unit_limit as u64; + + // The tip is a standalone transfer to a Jito fee account. + // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. + instructions.push(solana_sdk::system_instruction::transfer( + &payer.pubkey(), + // A random Jito fee account, taken from the getFeeAccount RPC response: + // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts + &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), + tip, + )); + } else { + instructions.push(ComputeBudgetInstruction::set_compute_unit_price( + compute_unit_price_micro_lamports, + )); + } + + instructions.push(instruction); + + let recent_blockhash = if let Some(blockhash) = blockhash { + blockhash + } else { + self.rpc() + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) + .await + .map_err(ChainCommunicationError::from_other)? + }; + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer], + recent_blockhash, + ); + + Ok(tx) + } + + async fn get_process_instruction( + &self, + message: &HyperlaneMessage, + metadata: &[u8], + commitment: CommitmentConfig, + ) -> ChainResult { + let recipient: Pubkey = message.recipient.0.into(); + let mut encoded_message = vec![]; + message.write_to(&mut encoded_message).unwrap(); + + let payer = self.get_payer()?; + + let (process_authority_key, _process_authority_bump) = Pubkey::try_find_program_address( + mailbox_process_authority_pda_seeds!(&recipient), + &self.program_id, + ) + .ok_or_else(|| { + ChainCommunicationError::from_other_str( + "Could not find program address for process authority", + ) + })?; + let (processed_message_account_key, _processed_message_account_bump) = + Pubkey::try_find_program_address( + mailbox_processed_message_pda_seeds!(message.id()), + &self.program_id, + ) + .ok_or_else(|| { + ChainCommunicationError::from_other_str( + "Could not find program address for processed message account", + ) + })?; + + // Get the account metas required for the recipient.InterchainSecurityModule instruction. + let ism_getter_account_metas = self.get_ism_getter_account_metas(recipient).await?; + + // Get the recipient ISM. + let ism = self + .get_recipient_ism(recipient, ism_getter_account_metas.clone()) + .await?; + + let ixn = + hyperlane_sealevel_mailbox::instruction::Instruction::InboxProcess(InboxProcess { + metadata: metadata.to_vec(), + message: encoded_message.clone(), + }); + let ixn_data = ixn + .into_instruction_data() + .map_err(ChainCommunicationError::from_other)?; + + // Craft the accounts for the transaction. + let mut accounts: Vec = vec![ + AccountMeta::new_readonly(payer.pubkey(), true), + AccountMeta::new_readonly(Pubkey::from_str(SYSTEM_PROGRAM).unwrap(), false), + AccountMeta::new(self.inbox.0, false), + AccountMeta::new_readonly(process_authority_key, false), + AccountMeta::new(processed_message_account_key, false), + ]; + accounts.extend(ism_getter_account_metas); + accounts.extend([ + AccountMeta::new_readonly(Pubkey::from_str(SPL_NOOP).unwrap(), false), + AccountMeta::new_readonly(ism, false), + ]); + + // Get the account metas required for the ISM.Verify instruction. + let ism_verify_account_metas = self + .get_ism_verify_account_metas(ism, metadata.into(), encoded_message) + .await?; + accounts.extend(ism_verify_account_metas); + + // The recipient. + accounts.extend([AccountMeta::new_readonly(recipient, false)]); + + // Get account metas required for the Handle instruction + let handle_account_metas = self.get_handle_account_metas(message).await?; + accounts.extend(handle_account_metas); + + let process_instruction = Instruction { + program_id: self.program_id, + data: ixn_data, + accounts, + }; + + Ok(process_instruction) + } + async fn send_and_confirm_transaction( &self, transaction: &Transaction, @@ -411,6 +616,12 @@ impl SealevelMailbox { .into_inner(); Ok(inbox) } + + fn get_payer(&self) -> ChainResult<&Keypair> { + self.payer + .as_ref() + .ok_or_else(|| ChainCommunicationError::SignerUnavailable) + } } impl HyperlaneContract for SealevelMailbox { @@ -490,132 +701,25 @@ impl Mailbox for SealevelMailbox { metadata: &[u8], _tx_gas_limit: Option, ) -> ChainResult { - let recipient: Pubkey = message.recipient.0.into(); - let mut encoded_message = vec![]; - message.write_to(&mut encoded_message).unwrap(); - - let payer = self - .payer - .as_ref() - .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?; - - let mut instructions = Vec::with_capacity(3); - // Set the compute unit limit. - instructions.push(ComputeBudgetInstruction::set_compute_unit_limit( - PROCESS_COMPUTE_UNITS, - )); - - // If we're using Jito, we need to send a tip to the Jito fee account. - // Otherwise, we need to set the compute unit price. - if self.use_jito() { - let tip: u64 = std::env::var("JITO_TIP_LAMPORTS") - .ok() - .and_then(|s| s.parse::().ok()) - .unwrap_or(PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX); - - // The tip is a standalone transfer to a Jito fee account. - // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. - instructions.push(solana_sdk::system_instruction::transfer( - &payer.pubkey(), - // A random Jito fee account, taken from the getFeeAccount RPC response: - // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts - &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), - tip, - )); - } // "processed" level commitment does not guarantee finality. // roughly 5% of blocks end up on a dropped fork. // However we don't want this function to be a bottleneck and there already // is retry logic in the agents. let commitment = CommitmentConfig::processed(); - let (process_authority_key, _process_authority_bump) = Pubkey::try_find_program_address( - mailbox_process_authority_pda_seeds!(&recipient), - &self.program_id, - ) - .ok_or_else(|| { - ChainCommunicationError::from_other_str( - "Could not find program address for process authority", - ) - })?; - let (processed_message_account_key, _processed_message_account_bump) = - Pubkey::try_find_program_address( - mailbox_processed_message_pda_seeds!(message.id()), - &self.program_id, - ) - .ok_or_else(|| { - ChainCommunicationError::from_other_str( - "Could not find program address for processed message account", - ) - })?; - - // Get the account metas required for the recipient.InterchainSecurityModule instruction. - let ism_getter_account_metas = self.get_ism_getter_account_metas(recipient).await?; - - // Get the recipient ISM. - let ism = self - .get_recipient_ism(recipient, ism_getter_account_metas.clone()) - .await?; - - let ixn = - hyperlane_sealevel_mailbox::instruction::Instruction::InboxProcess(InboxProcess { - metadata: metadata.to_vec(), - message: encoded_message.clone(), - }); - let ixn_data = ixn - .into_instruction_data() - .map_err(ChainCommunicationError::from_other)?; - - // Craft the accounts for the transaction. - let mut accounts: Vec = vec![ - AccountMeta::new_readonly(payer.pubkey(), true), - AccountMeta::new_readonly(Pubkey::from_str(SYSTEM_PROGRAM).unwrap(), false), - AccountMeta::new(self.inbox.0, false), - AccountMeta::new_readonly(process_authority_key, false), - AccountMeta::new(processed_message_account_key, false), - ]; - accounts.extend(ism_getter_account_metas); - accounts.extend([ - AccountMeta::new_readonly(Pubkey::from_str(SPL_NOOP).unwrap(), false), - AccountMeta::new_readonly(ism, false), - ]); - - // Get the account metas required for the ISM.Verify instruction. - let ism_verify_account_metas = self - .get_ism_verify_account_metas(ism, metadata.into(), encoded_message) + let process_instruction = self + .get_process_instruction(message, metadata, commitment) .await?; - accounts.extend(ism_verify_account_metas); - - // The recipient. - accounts.extend([AccountMeta::new_readonly(recipient, false)]); - // Get account metas required for the Handle instruction - let handle_account_metas = self.get_handle_account_metas(message).await?; - accounts.extend(handle_account_metas); - - let inbox_instruction = Instruction { - program_id: self.program_id, - data: ixn_data, - accounts, - }; - instructions.push(inbox_instruction); - let recent_blockhash = self - .rpc() - .get_latest_blockhash_with_commitment(commitment) + let tx = self + .build_estimated_tx_for_instruction(process_instruction) .await?; - let txn = Transaction::new_signed_with_payer( - &instructions, - Some(&payer.pubkey()), - &[payer], - recent_blockhash, - ); - - tracing::info!(?txn, "Created sealevel transaction to process message"); + tracing::info!(?tx, "Created sealevel transaction to process message"); - let signature = self.send_and_confirm_transaction(&txn).await?; + let signature = self.send_and_confirm_transaction(&tx).await?; - tracing::info!(?txn, ?signature, "Sealevel transaction sent"); + tracing::info!(?tx, ?signature, "Sealevel transaction sent"); let executed = self .rpc() diff --git a/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs new file mode 100644 index 0000000000..b9a59cb69c --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs @@ -0,0 +1,185 @@ +#![allow(dead_code)] + +use async_trait::async_trait; +use hyperlane_core::{ChainCommunicationError, ChainResult}; +use reqwest::Client; +use serde::Deserialize; +use solana_sdk::{bs58, transaction::Transaction}; + +use crate::{HeliusPriorityFeeLevel, HeliusPriorityFeeOracleConfig}; + +#[async_trait] +pub trait PriorityFeeOracle: Send + Sync { + async fn get_priority_fee(&self, transaction: &Transaction) -> ChainResult; +} + +#[derive(Debug, Clone)] +pub struct ConstantPriorityFeeOracle { + fee: u64, +} + +impl ConstantPriorityFeeOracle { + pub fn new(fee: u64) -> Self { + Self { fee } + } +} + +#[async_trait] +impl PriorityFeeOracle for ConstantPriorityFeeOracle { + async fn get_priority_fee(&self, _transaction: &Transaction) -> ChainResult { + Ok(self.fee) + } +} + +#[derive(Debug, Clone)] +pub struct HeliusPriorityFeeOracle { + client: Client, + config: HeliusPriorityFeeOracleConfig, +} + +impl HeliusPriorityFeeOracle { + pub fn new(config: HeliusPriorityFeeOracleConfig) -> Self { + Self { + client: reqwest::Client::new(), + config, + } + } +} + +#[async_trait] +impl PriorityFeeOracle for HeliusPriorityFeeOracle { + async fn get_priority_fee(&self, transaction: &Transaction) -> ChainResult { + let base58_tx = bs58::encode( + bincode::serialize(transaction).map_err(ChainCommunicationError::from_other)?, + ) + .into_string(); + + let request_body = serde_json::json!({ + "jsonrpc": "2.0", + "id": "1", + "method": "getPriorityFeeEstimate", + "params": [ + { + "transaction": base58_tx, + "options": { + "includeAllPriorityFeeLevels": true, + "transactionEncoding": "base58", + } + } + ], + }); + + let response = self + .client + .post(self.config.url.clone()) + .json(&request_body) + .send() + .await + .map_err(ChainCommunicationError::from_other)?; + + let response: JsonRpcResult = response + .json() + .await + .map_err(ChainCommunicationError::from_other)?; + + tracing::trace!(priority_fee_levels = ?response.result.priority_fee_levels, "Fetched priority fee levels"); + + let fee = response + .result + .priority_fee_levels + .get_priority_fee(&self.config.fee_level) + .ok_or_else(|| ChainCommunicationError::from_other_str("Priority fee level not found"))? + .round() as u64; + + Ok(fee) + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct JsonRpcResult { + jsonrpc: String, + id: String, + result: T, +} + +#[derive(Debug, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +struct GetPriorityFeeEstimateResult { + priority_fee_levels: PriorityFeeLevelsResponse, +} + +#[derive(Debug, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +struct PriorityFeeLevelsResponse { + min: Option, + low: Option, + medium: Option, + high: Option, + very_high: Option, + unsafe_max: Option, +} + +impl PriorityFeeLevelsResponse { + fn get_priority_fee(&self, level: &HeliusPriorityFeeLevel) -> Option { + match level { + HeliusPriorityFeeLevel::Min => self.min, + HeliusPriorityFeeLevel::Low => self.low, + HeliusPriorityFeeLevel::Medium => self.medium, + HeliusPriorityFeeLevel::High => self.high, + HeliusPriorityFeeLevel::VeryHigh => self.very_high, + HeliusPriorityFeeLevel::UnsafeMax => self.unsafe_max, + } + } +} + +#[cfg(test)] +mod test { + use solana_sdk::{bs58, transaction::Transaction}; + + use crate::priority_fee::{PriorityFeeLevelsResponse, PriorityFeeOracle}; + + use super::{GetPriorityFeeEstimateResult, JsonRpcResult}; + + #[tokio::test] + async fn test_helius_get_priority_fee() { + let helius_url = if let Ok(url) = std::env::var("HELIUS_URL") { + url + } else { + // Skip test if HELIUS_URL is not set + return; + }; + + let oracle = super::HeliusPriorityFeeOracle::new(super::HeliusPriorityFeeOracleConfig { + url: url::Url::parse(&helius_url).unwrap(), + fee_level: super::HeliusPriorityFeeLevel::Medium, + }); + + // Example process transaction + // https://solscan.io/tx/W9fXtRD8mPkkUmuoLi9QxSCgFuy32rCVa8kfxtPjWXWRH2D1AWzuDEGuvexWGyWhQDXnEmaADZMeYu5RVjWZyAB + let process_tx_base58 = "BPBE2dE4sPJX3nm4svEZ181qBfX9yvUp5H67uTt3aqRGtC6a77hW5vrQk9zJ3KkNuK63KoJCeqp1kkFwsbF5KL1UHf5Hrj8GXpiRxmKD8NybEZUWhjdVW9azMxJdnxxiFqH7wFQtZGkQxhx6oJz1qi5Xc64LEbPJEwSTAp5US1VCnnhWGRqJ297kvS8hWaVLuUxr4jEqYNG2LSusXZmzABBqEvRv753PBxcKiBE2moo9VKZ8n3ai6rmQGnSzsoAfwnjCx6iUdNSWqpYFHcq2xhMXJx8US5kv837KsT5tKQBbujsWUoRGGJ8vkmm7RJSYyR3DYEMa5ira9fiDwnK5qP3EgP2hrG73YYBxZ9naRrYzHG2GiEGWEUgNPHaUtK3JsbjTLiNjyZU8ERTdMxi4rBLppREJfHDWYUNgN9hTL81LYv4YoJY3UUTQphzT268f6oZoiyavngb8t3Lq8pbyc3gPiw7AcWXmn2ERDAcHvS59AaoxxcwZyn8UWUdynwCzvNbWhb97qVHSzBY1S79sxHFuqyBhbbD5YhkMhFGLjPUEDvncxE2hLt9iQCQaEQzCNRMmnZw7yJ1YxoKDKfmUTXJ6rmT4p2pz7f8x4jJwQ2pC2YxobcfHrNvD7929vXSvpomyZmaEXYAN2bqGBUe2KazpnobVCwafjKMVN4AaTJRMTXi92VKuShuKJEuZo9ZM7TScEqRZC5hLFU8SbCdASEUoQjpDzivUf1m9gQtT2ob5FPwJzcuZpqTWgixd59BRHTB1L5c4fDvtYr1QJFpJRN4DsXGryK4eTMu2oAs3imGpg1rHRLpuBTbcrchEivz7bD17bBj8VeHogfkPcehD9yaHzmYPRF47aWZ52GSFSSpc5kJRRQyghUKNPFBnycLGAbfkRYDdVzUgdrr3CNYksJCu45TChg54tMWWwrqSD3k5RPv7A6bXbAH4PzW83vzE2vGJFYpwUgNEnjuA1rVnYJHXsFdWBrqrsz3UvdTs5kUxyoxjNNKvoXSaTeXMXEt1HUdmQ3sw1dW9wRkYdHwWzksM6n7P7MLnVY6qv3BVUpJiX4K355BXhMhyozzcBQX2vvyC7J8UxPBofMrBRVtbMsXmfp3sphos1pog6wpN2MiEaJqm6KK5yQguANnQzN8mK7MREkjYXtCnczf84CrcHqpp2onQUaR4TPn8zCPVAxY4HVkCoDWTwKj8Am9M4L3a7wmF37epgKnQuypTH7dqbJPRTALe7tndrtvJCuoTFP8wPXQXxvwnBPXeLmhK9E2mpskTA33KfqvVBu4R5SFYNtGoKbvuHaDf83Lf2xx1YPUogXuEWZMx5zcaHWMmvutpfdnPe3Rb7GL4hPVKj4t9MNgiAg3QbjaR9nqYBUPT4kUpxVCJWEadDVh5pgLwnkg4DJ5ArNfgH5"; + let process_tx_bytes = bs58::decode(process_tx_base58).into_vec().unwrap(); + let transaction: Transaction = bincode::deserialize(&process_tx_bytes).unwrap(); + + oracle.get_priority_fee(&transaction).await.unwrap(); + } + + #[test] + fn test_helius_get_priority_fee_estimate_deser() { + let text = r#"{"jsonrpc":"2.0","result":{"priorityFeeLevels":{"min":0.0,"low":0.0,"medium":1000.0,"high":225000.0,"veryHigh":9000000.0,"unsafeMax":2340000000.0}},"id":"1"}"#; + let response: JsonRpcResult = + serde_json::from_str(text).unwrap(); + + let expected = GetPriorityFeeEstimateResult { + priority_fee_levels: PriorityFeeLevelsResponse { + min: Some(0.0), + low: Some(0.0), + medium: Some(1000.0), + high: Some(225000.0), + very_high: Some(9000000.0), + unsafe_max: Some(2340000000.0), + }, + }; + assert_eq!(response.result, expected); + } +} diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 59adb671f3..94424e15d7 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -2,8 +2,9 @@ use base64::Engine; use borsh::{BorshDeserialize, BorshSerialize}; use serializable_account_meta::{SerializableAccountMeta, SimulationReturnData}; use solana_client::{ - nonblocking::rpc_client::RpcClient, rpc_config::RpcBlockConfig, - rpc_config::RpcProgramAccountsConfig, rpc_config::RpcTransactionConfig, rpc_response::Response, + nonblocking::rpc_client::RpcClient, + rpc_config::{RpcBlockConfig, RpcProgramAccountsConfig, RpcTransactionConfig}, + rpc_response::{Response, RpcSimulateTransactionResult}, }; use solana_sdk::{ account::Account, @@ -17,7 +18,7 @@ use solana_sdk::{ }; use solana_transaction_status::{ EncodedConfirmedTransactionWithStatusMeta, TransactionStatus, UiConfirmedBlock, - UiReturnDataEncoding, UiTransactionEncoding, UiTransactionReturnData, + UiReturnDataEncoding, UiTransactionEncoding, }; use hyperlane_core::{ChainCommunicationError, ChainResult, U256}; @@ -227,9 +228,9 @@ impl SealevelRpcClient { Some(&payer.pubkey()), &recent_blockhash, )); - let return_data = self.simulate_transaction(&transaction).await?; + let simulation = self.simulate_transaction(&transaction).await?; - if let Some(return_data) = return_data { + if let Some(return_data) = simulation.return_data { let bytes = match return_data.data.1 { UiReturnDataEncoding::Base64 => base64::engine::general_purpose::STANDARD .decode(return_data.data.0) @@ -245,19 +246,18 @@ impl SealevelRpcClient { Ok(None) } - async fn simulate_transaction( + pub async fn simulate_transaction( &self, transaction: &Transaction, - ) -> ChainResult> { - let return_data = self + ) -> ChainResult { + let result = self .0 .simulate_transaction(transaction) .await .map_err(ChainCommunicationError::from_other)? - .value - .return_data; + .value; - Ok(return_data) + Ok(result) } } diff --git a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs index e0b7c5cb37..4a3ee86efe 100644 --- a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs +++ b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs @@ -1,6 +1,8 @@ use hyperlane_core::{config::OperationBatchConfig, ChainCommunicationError, NativeToken}; use url::Url; +use crate::priority_fee::{ConstantPriorityFeeOracle, HeliusPriorityFeeOracle, PriorityFeeOracle}; + /// Sealevel connection configuration #[derive(Debug, Clone)] pub struct ConnectionConf { @@ -10,6 +12,8 @@ pub struct ConnectionConf { pub operation_batch: OperationBatchConfig, /// Native token and its denomination pub native_token: NativeToken, + /// Priority fee oracle configuration + pub priority_fee_oracle: PriorityFeeOracleConfig, } /// An error type when parsing a connection configuration. @@ -23,6 +27,61 @@ pub enum ConnectionConfError { InvalidConnectionUrl(String, url::ParseError), } +/// Configuration to of how the priority fee should be determined +#[derive(Debug, Clone)] +pub enum PriorityFeeOracleConfig { + /// A constant value, in micro lamports + Constant(u64), + /// A Helius priority fee oracle + Helius(HeliusPriorityFeeOracleConfig), +} + +impl Default for PriorityFeeOracleConfig { + fn default() -> Self { + PriorityFeeOracleConfig::Constant(0) + } +} + +impl PriorityFeeOracleConfig { + /// Create a new priority fee oracle from the configuration + pub fn create_oracle(&self) -> Box { + match self { + PriorityFeeOracleConfig::Constant(fee) => { + Box::new(ConstantPriorityFeeOracle::new(*fee)) + } + PriorityFeeOracleConfig::Helius(config) => { + Box::new(HeliusPriorityFeeOracle::new(config.clone())) + } + } + } +} + +/// Configuration for the Helius priority fee oracle +#[derive(Debug, Clone)] +pub struct HeliusPriorityFeeOracleConfig { + /// The Helius URL to use + pub url: Url, + /// The fee level to use + pub fee_level: HeliusPriorityFeeLevel, +} + +/// The priority fee level to use +#[derive(Debug, Clone)] +pub enum HeliusPriorityFeeLevel { + /// 0th percentile + Min, + /// 10th percentile + Low, + /// 50th percentile + Medium, + /// 75th percentile + High, + /// 90th percentile + VeryHigh, + /// 100th percentile + UnsafeMax, +} + #[derive(thiserror::Error, Debug)] #[error(transparent)] struct SealevelNewConnectionError(#[from] anyhow::Error); diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index 70e1b81835..3d35543ab9 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -1,4 +1,7 @@ use eyre::eyre; +use hyperlane_sealevel::{ + HeliusPriorityFeeLevel, HeliusPriorityFeeOracleConfig, PriorityFeeOracleConfig, +}; use url::Url; use h_eth::TransactionOverrides; @@ -168,6 +171,7 @@ fn build_sealevel_connection_conf( url: url.clone(), operation_batch, native_token, + priority_fee_oracle: parse_priority_fee_oracle_config(chain, err), } } @@ -196,6 +200,65 @@ fn parse_native_token( } } +fn parse_priority_fee_oracle_config( + chain: &ValueParser, + err: &mut ConfigParsingError, +) -> PriorityFeeOracleConfig { + let priority_fee_oracle = chain + .chain(err) + .get_opt_key("priorityFeeOracle") + .end() + .map(|value_parser| { + let priority_fee_oracle = value_parser + .chain(err) + .get_key("type") + .parse_string() + .end() + .or_else(|| { + err.push( + &chain.cwp + "priorityFeeOracle.type", + eyre!("Missing priority fee oracle type"), + ); + None + }) + .unwrap_or_default(); + + match priority_fee_oracle { + "constant" => { + let fee = value_parser + .chain(err) + .get_key("fee") + .parse_u64() + .end() + .unwrap_or(0); + PriorityFeeOracleConfig::Constant(fee) + } + "helius" => { + let config = HeliusPriorityFeeOracleConfig { + url: value_parser + .chain(err) + .get_key("url") + .parse_from_str("Invalid url") + .end() + .unwrap(), + fee_level: HeliusPriorityFeeLevel::Medium, + }; + PriorityFeeOracleConfig::Helius(config) + } + _ => { + err.push( + &chain.cwp + "priorityFeeOracle.type", + eyre!("Unknown priority fee oracle type"), + ); + PriorityFeeOracleConfig::Constant(0) + } + } + }) + .unwrap_or_default(); + + priority_fee_oracle +} + pub fn build_connection_conf( domain_protocol: HyperlaneDomainProtocol, rpcs: &[Url], From b527bb8c38d5fee3d3d02807f12f09a0ecd86b90 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 5 Dec 2024 17:52:16 -0700 Subject: [PATCH 02/33] fee level parser --- .../src/settings/parser/connection_parser.rs | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index 3d35543ab9..2a030f1b3a 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -209,21 +209,21 @@ fn parse_priority_fee_oracle_config( .get_opt_key("priorityFeeOracle") .end() .map(|value_parser| { - let priority_fee_oracle = value_parser + let oracle_type = value_parser .chain(err) .get_key("type") .parse_string() .end() .or_else(|| { err.push( - &chain.cwp + "priorityFeeOracle.type", + &value_parser.cwp + "type", eyre!("Missing priority fee oracle type"), ); None }) .unwrap_or_default(); - match priority_fee_oracle { + match oracle_type { "constant" => { let fee = value_parser .chain(err) @@ -241,13 +241,13 @@ fn parse_priority_fee_oracle_config( .parse_from_str("Invalid url") .end() .unwrap(), - fee_level: HeliusPriorityFeeLevel::Medium, + fee_level: parse_helius_priority_fee_level(&value_parser, err), }; PriorityFeeOracleConfig::Helius(config) } _ => { err.push( - &chain.cwp + "priorityFeeOracle.type", + &value_parser.cwp + "type", eyre!("Unknown priority fee oracle type"), ); PriorityFeeOracleConfig::Constant(0) @@ -259,6 +259,34 @@ fn parse_priority_fee_oracle_config( priority_fee_oracle } +fn parse_helius_priority_fee_level( + value_parser: &ValueParser, + err: &mut ConfigParsingError, +) -> HeliusPriorityFeeLevel { + let level = value_parser + .chain(err) + .get_key("fee_level") + .parse_string() + .end() + .unwrap_or_default(); + + match level.to_lowercase().as_str() { + "min" => HeliusPriorityFeeLevel::Min, + "low" => HeliusPriorityFeeLevel::Low, + "medium" => HeliusPriorityFeeLevel::Medium, + "high" => HeliusPriorityFeeLevel::High, + "veryhigh" => HeliusPriorityFeeLevel::VeryHigh, + "unsafemax" => HeliusPriorityFeeLevel::UnsafeMax, + _ => { + err.push( + &value_parser.cwp + "level", + eyre!("Unknown priority fee level"), + ); + HeliusPriorityFeeLevel::Medium + } + } +} + pub fn build_connection_conf( domain_protocol: HyperlaneDomainProtocol, rpcs: &[Url], From 0e8e2c955fe8daac5caed8a06280649fe0d08e5e Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 5 Dec 2024 17:52:42 -0700 Subject: [PATCH 03/33] yeeter skeeter --- .../hyperlane-base/src/settings/parser/connection_parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index 2a030f1b3a..66fcabba69 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -279,7 +279,7 @@ fn parse_helius_priority_fee_level( "unsafemax" => HeliusPriorityFeeLevel::UnsafeMax, _ => { err.push( - &value_parser.cwp + "level", + &value_parser.cwp + "fee_level", eyre!("Unknown priority fee level"), ); HeliusPriorityFeeLevel::Medium From 106472f302083fc9073e42c2769f29df23365b9b Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 5 Dec 2024 18:10:15 -0700 Subject: [PATCH 04/33] only sign when needed --- .../chains/hyperlane-sealevel/src/mailbox.rs | 36 +++++++++---------- .../hyperlane-sealevel/src/rpc/client.rs | 13 +++++-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index ba15cff1c7..92989a67f9 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -310,12 +310,7 @@ impl SealevelMailbox { // for the compute unit limit and price because we want to include the instructions that // set these in the cost estimate. let simulation_tx = self - .create_transaction_for_instruction( - MAX_COMPUTE_UNITS, - 0, - Some(Hash::default()), - instruction.clone(), - ) + .create_transaction_for_instruction(MAX_COMPUTE_UNITS, 0, instruction.clone(), false) .await?; let simulation_result = self @@ -345,8 +340,8 @@ impl SealevelMailbox { .create_transaction_for_instruction( simulation_compute_units, priority_fee, - None, instruction, + true, ) .await?; @@ -359,8 +354,8 @@ impl SealevelMailbox { &self, compute_unit_limit: u32, compute_unit_price_micro_lamports: u64, - blockhash: Option, instruction: Instruction, + sign: bool, ) -> ChainResult { let payer = self.get_payer()?; @@ -392,21 +387,22 @@ impl SealevelMailbox { instructions.push(instruction); - let recent_blockhash = if let Some(blockhash) = blockhash { - blockhash - } else { - self.rpc() + let tx = if sign { + let recent_blockhash = self + .rpc() .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .await - .map_err(ChainCommunicationError::from_other)? - }; + .map_err(ChainCommunicationError::from_other)?; - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&payer.pubkey()), - &[payer], - recent_blockhash, - ); + Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer], + Hash::default(), + ) + } else { + Transaction::new_unsigned(Message::new(&instructions, Some(&payer.pubkey()))) + }; Ok(tx) } diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 94424e15d7..8f1f18434c 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -3,7 +3,10 @@ use borsh::{BorshDeserialize, BorshSerialize}; use serializable_account_meta::{SerializableAccountMeta, SimulationReturnData}; use solana_client::{ nonblocking::rpc_client::RpcClient, - rpc_config::{RpcBlockConfig, RpcProgramAccountsConfig, RpcTransactionConfig}, + rpc_config::{ + RpcBlockConfig, RpcProgramAccountsConfig, RpcSimulateTransactionConfig, + RpcTransactionConfig, + }, rpc_response::{Response, RpcSimulateTransactionResult}, }; use solana_sdk::{ @@ -252,7 +255,13 @@ impl SealevelRpcClient { ) -> ChainResult { let result = self .0 - .simulate_transaction(transaction) + .simulate_transaction_with_config( + transaction, + RpcSimulateTransactionConfig { + sig_verify: false, + ..Default::default() + }, + ) .await .map_err(ChainCommunicationError::from_other)? .value; From 55c0b9f8b9da00c57760c0a8312c64d0d45c6e30 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 5 Dec 2024 18:12:07 -0700 Subject: [PATCH 05/33] nit --- rust/main/chains/hyperlane-sealevel/src/mailbox.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index 92989a67f9..c6065ff767 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -335,6 +335,12 @@ impl SealevelMailbox { .get_priority_fee(&simulation_tx) .await?; + tracing::debug!( + ?priority_fee, + ?simulation_compute_units, + "Got priority fee and compute units for transaction" + ); + // Build the final transaction with the correct compute unit limit and price. let tx = self .create_transaction_for_instruction( From 8deaf03bc0dcb98fea2275630d3f420a3d3a02cb Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 5 Dec 2024 18:31:38 -0700 Subject: [PATCH 06/33] update schema, nit --- .../src/settings/parser/connection_parser.rs | 4 +-- typescript/sdk/src/metadata/agentConfig.ts | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index 66fcabba69..a269f99e5a 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -265,7 +265,7 @@ fn parse_helius_priority_fee_level( ) -> HeliusPriorityFeeLevel { let level = value_parser .chain(err) - .get_key("fee_level") + .get_key("feeLevel") .parse_string() .end() .unwrap_or_default(); @@ -279,7 +279,7 @@ fn parse_helius_priority_fee_level( "unsafemax" => HeliusPriorityFeeLevel::UnsafeMax, _ => { err.push( - &value_parser.cwp + "fee_level", + &value_parser.cwp + "feeLevel", eyre!("Unknown priority fee level"), ); HeliusPriorityFeeLevel::Medium diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index cb570e29eb..4e27631bf3 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -120,6 +120,24 @@ export type AgentCosmosGasPrice = z.infer< typeof AgentCosmosChainMetadataSchema >['gasPrice']; +const AgentSealevelChainMetadataSchema = z.object({ + priorityFeeOracle: z + .union([ + z.object({ + type: z.literal('helius'), + url: z.string().optional(), + // TODO add options + feeLevel: z.string().optional(), + }), + z.object({ + type: z.literal('constant'), + // In microlamports + fee: ZUWei, + }), + ]) + .optional(), +}); + export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( HyperlaneDeploymentArtifactsSchema, ) @@ -155,6 +173,7 @@ export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( .optional(), }) .merge(AgentCosmosChainMetadataSchema.partial()) + .merge(AgentSealevelChainMetadataSchema.partial()) .refine((metadata) => { // Make sure that the signer is valid for the protocol @@ -201,6 +220,13 @@ export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( } } + // If the protocol type is Sealevel, require everything in AgentSealevelChainMetadataSchema + if (metadata.protocol === ProtocolType.Sealevel) { + if (!AgentSealevelChainMetadataSchema.safeParse(metadata).success) { + return false; + } + } + return true; }); From d01cbf33e27e9b0fc8f33a289a861ecb9d190832 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 6 Dec 2024 11:33:13 -0700 Subject: [PATCH 07/33] some tweaks --- .../chains/hyperlane-sealevel/src/mailbox.rs | 3 +- .../hyperlane-sealevel/src/rpc/client.rs | 1 + typescript/infra/src/agents/index.ts | 31 +++++++++++++++++-- typescript/sdk/src/index.ts | 2 ++ typescript/sdk/src/metadata/agentConfig.ts | 22 ++++++++++--- 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index c6065ff767..ebd6311f97 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -318,6 +318,7 @@ impl SealevelMailbox { .rpc() .simulate_transaction(&simulation_tx) .await?; + tracing::warn!(?simulation_result, "Got simulation result for transaction"); let simulation_compute_units: u32 = simulation_result .units_consumed .ok_or_else(|| { @@ -335,7 +336,7 @@ impl SealevelMailbox { .get_priority_fee(&simulation_tx) .await?; - tracing::debug!( + tracing::warn!( ?priority_fee, ?simulation_compute_units, "Got priority fee and compute units for transaction" diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 8f1f18434c..8e5ee3920e 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -259,6 +259,7 @@ impl SealevelRpcClient { transaction, RpcSimulateTransactionConfig { sig_verify: false, + replace_recent_blockhash: true, ..Default::default() }, ) diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index b7be0e0098..e11f7210cd 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -1,8 +1,15 @@ import fs from 'fs'; import { join } from 'path'; -import { ChainName, RelayerConfig, RpcConsensusType } from '@hyperlane-xyz/sdk'; -import { objOmitKeys } from '@hyperlane-xyz/utils'; +import { + AgentChainMetadata, + AgentSealevelHeliusFeeLevel, + AgentSealevelPriorityFeeOracleType, + ChainName, + RelayerConfig, + RpcConsensusType, +} from '@hyperlane-xyz/sdk'; +import { ProtocolType, objOmitKeys } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; import { getChain } from '../../config/registry.js'; @@ -87,12 +94,32 @@ export abstract class AgentHelmManager extends HelmManager if (reorgPeriod === undefined) { throw new Error(`No reorg period found for chain ${chain}`); } + + let priorityFeeOracle: AgentChainMetadata['priorityFeeOracle']; + + if (getChain(chain).protocol === ProtocolType.Sealevel) { + if (chain === 'solanamainnet') { + priorityFeeOracle = { + type: AgentSealevelPriorityFeeOracleType.Helius, + feeLevel: AgentSealevelHeliusFeeLevel.Medium, + // URL is populated by the external secrets in the helm chart + url: '', + }; + } else { + priorityFeeOracle = { + type: AgentSealevelPriorityFeeOracleType.Constant, + fee: '0', + }; + } + } + return { name: chain, rpcConsensusType: this.rpcConsensusType(chain), protocol: metadata.protocol, blocks: { reorgPeriod }, maxBatchSize: 32, + priorityFeeOracle, }; }), }, diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 217b62aa87..aaf99adcc8 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -184,6 +184,8 @@ export { AgentCosmosGasPrice, AgentLogFormat, AgentLogLevel, + AgentSealevelHeliusFeeLevel, + AgentSealevelPriorityFeeOracleType, AgentSigner, AgentSignerAwsKey, AgentSignerHexKey, diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index 4e27631bf3..84543320dc 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -51,6 +51,20 @@ export enum AgentSignerKeyType { Cosmos = 'cosmosKey', } +export enum AgentSealevelPriorityFeeOracleType { + Helius = 'helius', + Constant = 'constant', +} + +export enum AgentSealevelHeliusFeeLevel { + Min = 'min', + Low = 'low', + Medium = 'medium', + High = 'high', + VeryHigh = 'veryHigh', + UnsafeMax = 'unsafeMax', +} + const AgentSignerHexKeySchema = z .object({ type: z.literal(AgentSignerKeyType.Hex).optional(), @@ -124,13 +138,13 @@ const AgentSealevelChainMetadataSchema = z.object({ priorityFeeOracle: z .union([ z.object({ - type: z.literal('helius'), - url: z.string().optional(), + type: z.literal(AgentSealevelPriorityFeeOracleType.Helius), + url: z.string(), // TODO add options - feeLevel: z.string().optional(), + feeLevel: z.nativeEnum(AgentSealevelHeliusFeeLevel), }), z.object({ - type: z.literal('constant'), + type: z.literal(AgentSealevelPriorityFeeOracleType.Constant), // In microlamports fee: ZUWei, }), From 7b7856c9ad4b9de2452912b3a0562d813d0e7ba7 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 6 Dec 2024 12:39:49 -0700 Subject: [PATCH 08/33] external secrinnit --- .../helm/hyperlane-agent/templates/external-secret.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rust/main/helm/hyperlane-agent/templates/external-secret.yaml b/rust/main/helm/hyperlane-agent/templates/external-secret.yaml index 9be0700b75..a3fdfd199b 100644 --- a/rust/main/helm/hyperlane-agent/templates/external-secret.yaml +++ b/rust/main/helm/hyperlane-agent/templates/external-secret.yaml @@ -30,6 +30,9 @@ spec: {{- if eq .protocol "cosmos" }} HYP_CHAINS_{{ .name | upper }}_CUSTOMGRPCURLS: {{ printf "'{{ .%s_grpcs | mustFromJson | join \",\" }}'" .name }} {{- end }} + {{- if and (eq .protocol "sealevel") (eq .priorityFeeOracle.type "helius") }} + HYP_CHAINS_{{ .name | upper }}_PRIORITYFEEORACLE_URL: {{ printf "'{{ .%s_helius }}'" .name }} + {{- end }} {{- end }} data: {{- /* @@ -45,4 +48,8 @@ spec: remoteRef: key: {{ printf "%s-grpc-endpoints-%s" $.Values.hyperlane.runEnv .name }} {{- end }} + {{- if and (eq .protocol "sealevel") (eq .priorityFeeOracle.type "helius") }} + - secretKey: {{ printf "%s_helius" .name }} + remoteRef: + key: {{ printf "%s-rpc-endpoint-helius-%s" $.Values.hyperlane.runEnv .name }} {{- end }} From c68b3146b41db77d90973a6b0f96b06af718518f Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 6 Dec 2024 12:44:09 -0700 Subject: [PATCH 09/33] oops --- rust/main/chains/hyperlane-sealevel/src/mailbox.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index ebd6311f97..af2aa8236c 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -405,7 +405,7 @@ impl SealevelMailbox { &instructions, Some(&payer.pubkey()), &[payer], - Hash::default(), + recent_blockhash, ) } else { Transaction::new_unsigned(Message::new(&instructions, Some(&payer.pubkey()))) From 8e6427cdb4546453de2444e38b7093f3e04ecaab Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 6 Dec 2024 13:26:36 -0700 Subject: [PATCH 10/33] config stuff --- .../config/environments/mainnet3/agent.ts | 25 +++++++++++++++++++ typescript/infra/src/agents/index.ts | 17 +++---------- typescript/infra/src/config/agent/agent.ts | 4 +++ typescript/sdk/src/index.ts | 2 ++ typescript/sdk/src/metadata/agentConfig.ts | 6 +++++ 5 files changed, 41 insertions(+), 13 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 58c42ffd81..a94d0ee74e 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -1,4 +1,8 @@ import { + AgentSealevelHeliusFeeLevel, + AgentSealevelPriorityFeeOracle, + AgentSealevelPriorityFeeOracleType, + ChainName, GasPaymentEnforcement, GasPaymentEnforcementPolicyType, RpcConsensusType, @@ -438,6 +442,26 @@ const metricAppContextsGetter = (): MetricAppContext[] => { ]; }; +const sealevelPriorityFeeOracleConfigGetter = ( + chain: ChainName, +): AgentSealevelPriorityFeeOracle => { + // Special case for Solana mainnet + if (chain === 'solanamainnet') { + return { + type: AgentSealevelPriorityFeeOracleType.Helius, + feeLevel: AgentSealevelHeliusFeeLevel.Medium, + // URL is populated by the external secrets in the helm chart + url: '', + }; + } + + // For all other chains, we use the constant fee oracle with a fee of 0 + return { + type: AgentSealevelPriorityFeeOracleType.Constant, + fee: '0', + }; +}; + // Resource requests are based on observed usage found in https://abacusworks.grafana.net/d/FSR9YWr7k const relayerResources = { requests: { @@ -465,6 +489,7 @@ const hyperlane: RootAgentConfig = { context: Contexts.Hyperlane, contextChainNames: hyperlaneContextAgentChainNames, rolesWithKeys: ALL_KEY_ROLES, + sealevelPriorityFeeOracleConfigGetter, relayer: { rpcConsensusType: RpcConsensusType.Fallback, docker: { diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index e11f7210cd..4ba9585e9b 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -98,19 +98,10 @@ export abstract class AgentHelmManager extends HelmManager let priorityFeeOracle: AgentChainMetadata['priorityFeeOracle']; if (getChain(chain).protocol === ProtocolType.Sealevel) { - if (chain === 'solanamainnet') { - priorityFeeOracle = { - type: AgentSealevelPriorityFeeOracleType.Helius, - feeLevel: AgentSealevelHeliusFeeLevel.Medium, - // URL is populated by the external secrets in the helm chart - url: '', - }; - } else { - priorityFeeOracle = { - type: AgentSealevelPriorityFeeOracleType.Constant, - fee: '0', - }; - } + priorityFeeOracle = + this.config.rawConfig.sealevelPriorityFeeOracleConfigGetter?.( + chain, + ); } return { diff --git a/typescript/infra/src/config/agent/agent.ts b/typescript/infra/src/config/agent/agent.ts index 987a05f0ea..4a59145096 100644 --- a/typescript/infra/src/config/agent/agent.ts +++ b/typescript/infra/src/config/agent/agent.ts @@ -1,5 +1,6 @@ import { AgentChainMetadata, + AgentSealevelPriorityFeeOracle, AgentSignerAwsKey, AgentSignerKeyType, ChainName, @@ -85,6 +86,9 @@ export interface AgentContextConfig extends AgentEnvConfig { rolesWithKeys: Role[]; // Names of chains this context cares about (subset of environmentChainNames) contextChainNames: AgentChainNames; + sealevelPriorityFeeOracleConfigGetter?: ( + chain: ChainName, + ) => AgentSealevelPriorityFeeOracle; } // incomplete common agent configuration for a role diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index aaf99adcc8..9fef78a18f 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -184,7 +184,9 @@ export { AgentCosmosGasPrice, AgentLogFormat, AgentLogLevel, + AgentSealevelChainMetadata, AgentSealevelHeliusFeeLevel, + AgentSealevelPriorityFeeOracle, AgentSealevelPriorityFeeOracleType, AgentSigner, AgentSignerAwsKey, diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index 84543320dc..1b7d277cdb 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -152,6 +152,12 @@ const AgentSealevelChainMetadataSchema = z.object({ .optional(), }); +export type AgentSealevelChainMetadata = z.infer< + typeof AgentSealevelChainMetadataSchema +>; +export type AgentSealevelPriorityFeeOracle = + AgentSealevelChainMetadata['priorityFeeOracle']; + export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( HyperlaneDeploymentArtifactsSchema, ) From e5566298ac816cab38979acc2cd5c01f6cc3ac8e Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 6 Dec 2024 22:25:28 -0700 Subject: [PATCH 11/33] drivebys --- .../config/environments/mainnet3/agent.ts | 45 ++++++++++--------- typescript/infra/src/utils/helm.ts | 9 ++-- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index a94d0ee74e..d267f9a247 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -362,6 +362,26 @@ export const hyperlaneContextAgentChainNames = getAgentChainNamesFromConfig( mainnet3SupportedChainNames, ); +const sealevelPriorityFeeOracleConfigGetter = ( + chain: ChainName, +): AgentSealevelPriorityFeeOracle => { + // Special case for Solana mainnet + if (chain === 'solanamainnet') { + return { + type: AgentSealevelPriorityFeeOracleType.Helius, + feeLevel: AgentSealevelHeliusFeeLevel.High, + // URL is populated by the external secrets in the helm chart + url: '', + }; + } + + // For all other chains, we use the constant fee oracle with a fee of 0 + return { + type: AgentSealevelPriorityFeeOracleType.Constant, + fee: '0', + }; +}; + const contextBase = { namespace: environment, runEnv: environment, @@ -369,6 +389,7 @@ const contextBase = { aws: { region: 'us-east-1', }, + sealevelPriorityFeeOracleConfigGetter, } as const; const gasPaymentEnforcement: GasPaymentEnforcement[] = [ @@ -442,26 +463,6 @@ const metricAppContextsGetter = (): MetricAppContext[] => { ]; }; -const sealevelPriorityFeeOracleConfigGetter = ( - chain: ChainName, -): AgentSealevelPriorityFeeOracle => { - // Special case for Solana mainnet - if (chain === 'solanamainnet') { - return { - type: AgentSealevelPriorityFeeOracleType.Helius, - feeLevel: AgentSealevelHeliusFeeLevel.Medium, - // URL is populated by the external secrets in the helm chart - url: '', - }; - } - - // For all other chains, we use the constant fee oracle with a fee of 0 - return { - type: AgentSealevelPriorityFeeOracleType.Constant, - fee: '0', - }; -}; - // Resource requests are based on observed usage found in https://abacusworks.grafana.net/d/FSR9YWr7k const relayerResources = { requests: { @@ -489,7 +490,6 @@ const hyperlane: RootAgentConfig = { context: Contexts.Hyperlane, contextChainNames: hyperlaneContextAgentChainNames, rolesWithKeys: ALL_KEY_ROLES, - sealevelPriorityFeeOracleConfigGetter, relayer: { rpcConsensusType: RpcConsensusType.Fallback, docker: { @@ -499,6 +499,7 @@ const hyperlane: RootAgentConfig = { gasPaymentEnforcement: gasPaymentEnforcement, metricAppContextsGetter, resources: relayerResources, + blacklist: [...warpRouteMatchingList('WIF/eclipsemainnet-solanamainnet')], }, validators: { docker: { @@ -528,7 +529,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '4cb2c9a-20241205-142854', + tag: '8e6427c-20241206-202715', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. diff --git a/typescript/infra/src/utils/helm.ts b/typescript/infra/src/utils/helm.ts index 2ec09385ea..15d8739e75 100644 --- a/typescript/infra/src/utils/helm.ts +++ b/typescript/infra/src/utils/helm.ts @@ -13,12 +13,13 @@ export enum HelmCommand { } export function helmifyValues(config: any, prefix?: string): string[] { + if (config === null || config === undefined) { + return []; + } + if (typeof config !== 'object') { // Helm incorrectly splits on unescaped commas. - const value = - config !== undefined - ? JSON.stringify(config).replaceAll(',', '\\,') - : undefined; + const value = JSON.stringify(config).replaceAll(',', '\\,'); return [`--set ${prefix}=${value}`]; } From b12bb5c24459848be0abdc549424545d8b605358 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 6 Dec 2024 22:47:57 -0700 Subject: [PATCH 12/33] cmon --- rust/main/chains/hyperlane-sealevel/src/priority_fee.rs | 2 +- .../main/helm/hyperlane-agent/templates/external-secret.yaml | 5 +++-- typescript/infra/config/environments/mainnet3/agent.ts | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs index b9a59cb69c..a58c6530b4 100644 --- a/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs +++ b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs @@ -82,7 +82,7 @@ impl PriorityFeeOracle for HeliusPriorityFeeOracle { .await .map_err(ChainCommunicationError::from_other)?; - tracing::trace!(priority_fee_levels = ?response.result.priority_fee_levels, "Fetched priority fee levels"); + tracing::warn!(priority_fee_levels = ?response.result.priority_fee_levels, "Fetched priority fee levels"); let fee = response .result diff --git a/rust/main/helm/hyperlane-agent/templates/external-secret.yaml b/rust/main/helm/hyperlane-agent/templates/external-secret.yaml index a3fdfd199b..7d169c40f8 100644 --- a/rust/main/helm/hyperlane-agent/templates/external-secret.yaml +++ b/rust/main/helm/hyperlane-agent/templates/external-secret.yaml @@ -30,7 +30,7 @@ spec: {{- if eq .protocol "cosmos" }} HYP_CHAINS_{{ .name | upper }}_CUSTOMGRPCURLS: {{ printf "'{{ .%s_grpcs | mustFromJson | join \",\" }}'" .name }} {{- end }} - {{- if and (eq .protocol "sealevel") (eq .priorityFeeOracle.type "helius") }} + {{- if and (eq .protocol "sealevel") (eq ((.priorityFeeOracle).type) "helius") }} HYP_CHAINS_{{ .name | upper }}_PRIORITYFEEORACLE_URL: {{ printf "'{{ .%s_helius }}'" .name }} {{- end }} {{- end }} @@ -48,8 +48,9 @@ spec: remoteRef: key: {{ printf "%s-grpc-endpoints-%s" $.Values.hyperlane.runEnv .name }} {{- end }} - {{- if and (eq .protocol "sealevel") (eq .priorityFeeOracle.type "helius") }} + {{- if and (eq .protocol "sealevel") (eq ((.priorityFeeOracle).type) "helius") }} - secretKey: {{ printf "%s_helius" .name }} remoteRef: key: {{ printf "%s-rpc-endpoint-helius-%s" $.Values.hyperlane.runEnv .name }} {{- end }} + {{- end }} diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index d267f9a247..45ae4787eb 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -529,7 +529,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '8e6427c-20241206-202715', + tag: 'e556629-20241207-052607', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. From 2c881f7588fa667a34f796e2ceb0665c87848e4e Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 9 Dec 2024 18:50:13 -0700 Subject: [PATCH 13/33] test the anti-jito hunch --- .../chains/hyperlane-sealevel/src/mailbox.rs | 39 ++- .../config/environments/mainnet3/agent.ts | 4 +- .../mainnet3/aw-validators/hyperlane.json | 324 +++++++++++++----- .../mainnet3/aw-validators/rc.json | 144 ++++++-- 4 files changed, 382 insertions(+), 129 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index af2aa8236c..bd03803398 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -110,6 +110,7 @@ pub struct SealevelMailbox { pub(crate) provider: SealevelProvider, payer: Option, priority_fee_oracle: Box, + priority_fee_oracle_config: PriorityFeeOracleConfig, } impl SealevelMailbox { @@ -137,6 +138,7 @@ impl SealevelMailbox { provider, payer, priority_fee_oracle: conf.priority_fee_oracle.create_oracle(), + priority_fee_oracle_config: conf.priority_fee_oracle.clone(), }) } @@ -293,7 +295,7 @@ impl SealevelMailbox { self.get_account_metas(instruction).await } - fn use_jito(&self) -> bool { + fn is_solana(&self) -> bool { matches!( self.domain(), HyperlaneDomain::Known(KnownHyperlaneDomain::SolanaMainnet) @@ -374,7 +376,7 @@ impl SealevelMailbox { // If we're using Jito, we need to send a tip to the Jito fee account. // Otherwise, we need to set the compute unit price. - if self.use_jito() { + if self.is_solana() { let tip: u64 = compute_unit_price_micro_lamports * compute_unit_limit as u64; // The tip is a standalone transfer to a Jito fee account. @@ -503,15 +505,32 @@ impl SealevelMailbox { &self, transaction: &Transaction, ) -> ChainResult { - if self.use_jito() { - self.send_and_confirm_transaction_with_jito(transaction) - .await - } else { - self.provider - .rpc() - .send_and_confirm_transaction(transaction) - .await + // if self.is_solana() { + // self.send_and_confirm_transaction_with_jito(transaction) + // .await + // } else { + // self.provider + // .rpc() + // .send_and_confirm_transaction(transaction) + // .await + // } + + if self.is_solana() { + if let PriorityFeeOracleConfig::Helius(helius) = &self.priority_fee_oracle_config { + let rpc = SealevelRpcClient::new(helius.url.clone().into()); + return self + .provider + .rpc() + .send_and_confirm_transaction(transaction) + .await; + } else { + tracing::warn!("Priority fee oracle is not Helius, falling back to normal RPC"); + } } + self.provider + .rpc() + .send_and_confirm_transaction(transaction) + .await } // Stolen from Solana's non-blocking client, but with Jito! diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 45ae4787eb..ad9a3f85ac 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -499,7 +499,7 @@ const hyperlane: RootAgentConfig = { gasPaymentEnforcement: gasPaymentEnforcement, metricAppContextsGetter, resources: relayerResources, - blacklist: [...warpRouteMatchingList('WIF/eclipsemainnet-solanamainnet')], + // blacklist: [...warpRouteMatchingList('WIF/eclipsemainnet-solanamainnet')], }, validators: { docker: { @@ -529,7 +529,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'e556629-20241207-052607', + tag: 'b12bb5c-20241207-054843', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json index 9e39d900ce..c79aa3ea0d 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json @@ -1,15 +1,23 @@ { "ancient8": { - "validators": ["0xbb5842ae0e05215b53df4787a29144efb7e67551"] + "validators": [ + "0xbb5842ae0e05215b53df4787a29144efb7e67551" + ] }, "alephzeroevmmainnet": { - "validators": ["0x33f20e6e775747d60301c6ea1c50e51f0389740c"] + "validators": [ + "0x33f20e6e775747d60301c6ea1c50e51f0389740c" + ] }, "apechain": { - "validators": ["0x773d7fe6ffb1ba4de814c28044ff9a2d83a48221"] + "validators": [ + "0x773d7fe6ffb1ba4de814c28044ff9a2d83a48221" + ] }, "appchain": { - "validators": ["0x0531251bbadc1f9f19ccce3ca6b3f79f08eae1be"] + "validators": [ + "0x0531251bbadc1f9f19ccce3ca6b3f79f08eae1be" + ] }, "arbitrum": { "validators": [ @@ -19,16 +27,24 @@ ] }, "arbitrumnova": { - "validators": ["0xd2a5e9123308d187383c87053811a2c21bd8af1f"] + "validators": [ + "0xd2a5e9123308d187383c87053811a2c21bd8af1f" + ] }, "astar": { - "validators": ["0x4d1b2cade01ee3493f44304653d8e352c66ec3e7"] + "validators": [ + "0x4d1b2cade01ee3493f44304653d8e352c66ec3e7" + ] }, "astarzkevm": { - "validators": ["0x89ecdd6caf138934bf3a2fb7b323984d72fd66de"] + "validators": [ + "0x89ecdd6caf138934bf3a2fb7b323984d72fd66de" + ] }, "flame": { - "validators": ["0x1fa928ce884fa16357d4b8866e096392d4d81f43"] + "validators": [ + "0x1fa928ce884fa16357d4b8866e096392d4d81f43" + ] }, "avalanche": { "validators": [ @@ -38,7 +54,9 @@ ] }, "b3": { - "validators": ["0xd77b516730a836fc41934e7d5864e72c165b934e"] + "validators": [ + "0xd77b516730a836fc41934e7d5864e72c165b934e" + ] }, "base": { "validators": [ @@ -48,16 +66,24 @@ ] }, "bitlayer": { - "validators": ["0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f"] + "validators": [ + "0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f" + ] }, "blast": { - "validators": ["0xf20c0b09f597597c8d2430d3d72dfddaf09177d1"] + "validators": [ + "0xf20c0b09f597597c8d2430d3d72dfddaf09177d1" + ] }, "bob": { - "validators": ["0x20f283be1eb0e81e22f51705dcb79883cfdd34aa"] + "validators": [ + "0x20f283be1eb0e81e22f51705dcb79883cfdd34aa" + ] }, "boba": { - "validators": ["0xebeb92c94ca8408e73aa16fd554cb3a7df075c59"] + "validators": [ + "0xebeb92c94ca8408e73aa16fd554cb3a7df075c59" + ] }, "bsc": { "validators": [ @@ -67,7 +93,9 @@ ] }, "bsquared": { - "validators": ["0xcadc90933c9fbe843358a4e70e46ad2db78e28aa"] + "validators": [ + "0xcadc90933c9fbe843358a4e70e46ad2db78e28aa" + ] }, "celo": { "validators": [ @@ -77,31 +105,49 @@ ] }, "cheesechain": { - "validators": ["0x478fb53c6860ae8fc35235ba0d38d49b13128226"] + "validators": [ + "0x478fb53c6860ae8fc35235ba0d38d49b13128226" + ] }, "chilizmainnet": { - "validators": ["0x7403e5d58b48b0f5f715d9c78fbc581f01a625cb"] + "validators": [ + "0x7403e5d58b48b0f5f715d9c78fbc581f01a625cb" + ] }, "coredao": { - "validators": ["0xbd6e158a3f5830d99d7d2bce192695bc4a148de2"] + "validators": [ + "0xbd6e158a3f5830d99d7d2bce192695bc4a148de2" + ] }, "cyber": { - "validators": ["0x94d7119ceeb802173b6924e6cc8c4cd731089a27"] + "validators": [ + "0x94d7119ceeb802173b6924e6cc8c4cd731089a27" + ] }, "degenchain": { - "validators": ["0x433e311f19524cd64fb2123ad0aa1579a4e1fc83"] + "validators": [ + "0x433e311f19524cd64fb2123ad0aa1579a4e1fc83" + ] }, "dogechain": { - "validators": ["0xe43f742c37858746e6d7e458bc591180d0cba440"] + "validators": [ + "0xe43f742c37858746e6d7e458bc591180d0cba440" + ] }, "duckchain": { - "validators": ["0x91d55fe6dac596a6735d96365e21ce4bca21d83c"] + "validators": [ + "0x91d55fe6dac596a6735d96365e21ce4bca21d83c" + ] }, "eclipsemainnet": { - "validators": ["0xebb52d7eaa3ff7a5a6260bfe5111ce52d57401d0"] + "validators": [ + "0xebb52d7eaa3ff7a5a6260bfe5111ce52d57401d0" + ] }, "endurance": { - "validators": ["0x28c5b322da06f184ebf68693c5d19df4d4af13e5"] + "validators": [ + "0x28c5b322da06f184ebf68693c5d19df4d4af13e5" + ] }, "ethereum": { "validators": [ @@ -111,22 +157,34 @@ ] }, "everclear": { - "validators": ["0xeff20ae3d5ab90abb11e882cfce4b92ea6c74837"] + "validators": [ + "0xeff20ae3d5ab90abb11e882cfce4b92ea6c74837" + ] }, "fantom": { - "validators": ["0xa779572028e634e16f26af5dfd4fa685f619457d"] + "validators": [ + "0xa779572028e634e16f26af5dfd4fa685f619457d" + ] }, "flare": { - "validators": ["0xb65e52be342dba3ab2c088ceeb4290c744809134"] + "validators": [ + "0xb65e52be342dba3ab2c088ceeb4290c744809134" + ] }, "flowmainnet": { - "validators": ["0xe132235c958ca1f3f24d772e5970dd58da4c0f6e"] + "validators": [ + "0xe132235c958ca1f3f24d772e5970dd58da4c0f6e" + ] }, "fraxtal": { - "validators": ["0x4bce180dac6da60d0f3a2bdf036ffe9004f944c1"] + "validators": [ + "0x4bce180dac6da60d0f3a2bdf036ffe9004f944c1" + ] }, "fusemainnet": { - "validators": ["0x770c8ec9aac8cec4b2ead583b49acfbc5a1cf8a9"] + "validators": [ + "0x770c8ec9aac8cec4b2ead583b49acfbc5a1cf8a9" + ] }, "gnosis": { "validators": [ @@ -136,13 +194,19 @@ ] }, "gravity": { - "validators": ["0x23d549bf757a02a6f6068e9363196ecd958c974e"] + "validators": [ + "0x23d549bf757a02a6f6068e9363196ecd958c974e" + ] }, "harmony": { - "validators": ["0xd677803a67651974b1c264171b5d7ca8838db8d5"] + "validators": [ + "0xd677803a67651974b1c264171b5d7ca8838db8d5" + ] }, "immutablezkevmmainnet": { - "validators": ["0xbdda85b19a5efbe09e52a32db1a072f043dd66da"] + "validators": [ + "0xbdda85b19a5efbe09e52a32db1a072f043dd66da" + ] }, "inevm": { "validators": [ @@ -152,28 +216,44 @@ ] }, "injective": { - "validators": ["0xbfb8911b72cfb138c7ce517c57d9c691535dc517"] + "validators": [ + "0xbfb8911b72cfb138c7ce517c57d9c691535dc517" + ] }, "kaia": { - "validators": ["0x9de0b3abb221d19719882fa4d61f769fdc2be9a4"] + "validators": [ + "0x9de0b3abb221d19719882fa4d61f769fdc2be9a4" + ] }, "kroma": { - "validators": ["0x71b83c21342787d758199e4b8634d3a15f02dc6e"] + "validators": [ + "0x71b83c21342787d758199e4b8634d3a15f02dc6e" + ] }, "linea": { - "validators": ["0xf2d5409a59e0f5ae7635aff73685624904a77d94"] + "validators": [ + "0xf2d5409a59e0f5ae7635aff73685624904a77d94" + ] }, "lisk": { - "validators": ["0xc0b282aa5bac43fee83cf71dc3dd1797c1090ea5"] + "validators": [ + "0xc0b282aa5bac43fee83cf71dc3dd1797c1090ea5" + ] }, "lukso": { - "validators": ["0xa5e953701dcddc5b958b5defb677a829d908df6d"] + "validators": [ + "0xa5e953701dcddc5b958b5defb677a829d908df6d" + ] }, "lumia": { - "validators": ["0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f"] + "validators": [ + "0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f" + ] }, "lumiaprism": { - "validators": ["0xb69731640ffd4338a2c9358a935b0274c6463f85"] + "validators": [ + "0xb69731640ffd4338a2c9358a935b0274c6463f85" + ] }, "mantapacific": { "validators": [ @@ -183,25 +263,39 @@ ] }, "mantle": { - "validators": ["0xf930636c5a1a8bf9302405f72e3af3c96ebe4a52"] + "validators": [ + "0xf930636c5a1a8bf9302405f72e3af3c96ebe4a52" + ] }, "merlin": { - "validators": ["0xc1d6600cb9326ed2198cc8c4ba8d6668e8671247"] + "validators": [ + "0xc1d6600cb9326ed2198cc8c4ba8d6668e8671247" + ] }, "metal": { - "validators": ["0xd9f7f1a05826197a93df51e86cefb41dfbfb896a"] + "validators": [ + "0xd9f7f1a05826197a93df51e86cefb41dfbfb896a" + ] }, "metis": { - "validators": ["0xc4a3d25107060e800a43842964546db508092260"] + "validators": [ + "0xc4a3d25107060e800a43842964546db508092260" + ] }, "mint": { - "validators": ["0xfed01ccdd7a65e8a6ad867b7fb03b9eb47777ac9"] + "validators": [ + "0xfed01ccdd7a65e8a6ad867b7fb03b9eb47777ac9" + ] }, "mode": { - "validators": ["0x7eb2e1920a4166c19d6884c1cec3d2cf356fc9b7"] + "validators": [ + "0x7eb2e1920a4166c19d6884c1cec3d2cf356fc9b7" + ] }, "molten": { - "validators": ["0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f"] + "validators": [ + "0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f" + ] }, "moonbeam": { "validators": [ @@ -211,7 +305,9 @@ ] }, "morph": { - "validators": ["0x4884535f393151ec419add872100d352f71af380"] + "validators": [ + "0x4884535f393151ec419add872100d352f71af380" + ] }, "neutron": { "validators": [ @@ -221,7 +317,9 @@ ] }, "oortmainnet": { - "validators": ["0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7"] + "validators": [ + "0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7" + ] }, "optimism": { "validators": [ @@ -231,10 +329,14 @@ ] }, "orderly": { - "validators": ["0xec3dc91f9fa2ad35edf5842aa764d5573b778bb6"] + "validators": [ + "0xec3dc91f9fa2ad35edf5842aa764d5573b778bb6" + ] }, "osmosis": { - "validators": ["0xea483af11c19fa41b16c31d1534c2a486a92bcac"] + "validators": [ + "0xea483af11c19fa41b16c31d1534c2a486a92bcac" + ] }, "polygon": { "validators": [ @@ -251,28 +353,44 @@ ] }, "polynomialfi": { - "validators": ["0x23d348c2d365040e56f3fee07e6897122915f513"] + "validators": [ + "0x23d348c2d365040e56f3fee07e6897122915f513" + ] }, "prom": { - "validators": ["0xb0c4042b7c9a95345be8913f4cdbf4043b923d98"] + "validators": [ + "0xb0c4042b7c9a95345be8913f4cdbf4043b923d98" + ] }, "proofofplay": { - "validators": ["0xcda40baa71970a06e5f55e306474de5ca4e21c3b"] + "validators": [ + "0xcda40baa71970a06e5f55e306474de5ca4e21c3b" + ] }, "rarichain": { - "validators": ["0xeac012df7530720dd7d6f9b727e4fe39807d1516"] + "validators": [ + "0xeac012df7530720dd7d6f9b727e4fe39807d1516" + ] }, "real": { - "validators": ["0xaebadd4998c70b05ce8715cf0c3cb8862fe0beec"] + "validators": [ + "0xaebadd4998c70b05ce8715cf0c3cb8862fe0beec" + ] }, "redstone": { - "validators": ["0x1400b9737007f7978d8b4bbafb4a69c83f0641a7"] + "validators": [ + "0x1400b9737007f7978d8b4bbafb4a69c83f0641a7" + ] }, "rootstockmainnet": { - "validators": ["0x8675eb603d62ab64e3efe90df914e555966e04ac"] + "validators": [ + "0x8675eb603d62ab64e3efe90df914e555966e04ac" + ] }, "sanko": { - "validators": ["0x795c37d5babbc44094b084b0c89ed9db9b5fae39"] + "validators": [ + "0x795c37d5babbc44094b084b0c89ed9db9b5fae39" + ] }, "scroll": { "validators": [ @@ -282,69 +400,113 @@ ] }, "sei": { - "validators": ["0x9920d2dbf6c85ffc228fdc2e810bf895732c6aa5"] + "validators": [ + "0x9920d2dbf6c85ffc228fdc2e810bf895732c6aa5" + ] }, "shibarium": { - "validators": ["0xfa33391ee38597cbeef72ccde8c9e13e01e78521"] + "validators": [ + "0xfa33391ee38597cbeef72ccde8c9e13e01e78521" + ] }, "snaxchain": { - "validators": ["0x2c25829ae32a772d2a49f6c4b34f8b01fd03ef9e"] + "validators": [ + "0x2c25829ae32a772d2a49f6c4b34f8b01fd03ef9e" + ] }, "solanamainnet": { - "validators": ["0x28464752829b3ea59a497fca0bdff575c534c3ff"] + "validators": [ + "0x28464752829b3ea59a497fca0bdff575c534c3ff" + ] }, "superseed": { - "validators": ["0xdc2b87cb555411bb138d3a4e5f7832c87fae2b88"] + "validators": [ + "0xdc2b87cb555411bb138d3a4e5f7832c87fae2b88" + ] }, "superpositionmainnet": { - "validators": ["0x3f489acdd341c6b4dd86293fa2cc5ecc8ccf4f84"] + "validators": [ + "0x3f489acdd341c6b4dd86293fa2cc5ecc8ccf4f84" + ] }, "swell": { - "validators": ["0x4f51e4f4c7fb45d82f91568480a1a2cfb69216ed"] + "validators": [ + "0x4f51e4f4c7fb45d82f91568480a1a2cfb69216ed" + ] }, "taiko": { - "validators": ["0xa930073c8f2d0b2f7423ea32293e0d1362e65d79"] + "validators": [ + "0xa930073c8f2d0b2f7423ea32293e0d1362e65d79" + ] }, "tangle": { - "validators": ["0x1ee52cbbfacd7dcb0ba4e91efaa6fbc61602b15b"] + "validators": [ + "0x1ee52cbbfacd7dcb0ba4e91efaa6fbc61602b15b" + ] }, "treasure": { - "validators": ["0x6ad994819185553e8baa01533f0cd2c7cadfe6cc"] + "validators": [ + "0x6ad994819185553e8baa01533f0cd2c7cadfe6cc" + ] }, "unichain": { - "validators": ["0x9773a382342ebf604a2e5de0a1f462fb499e28b1"] + "validators": [ + "0x9773a382342ebf604a2e5de0a1f462fb499e28b1" + ] }, "vana": { - "validators": ["0xfdf3b0dfd4b822d10cacb15c8ae945ea269e7534"] + "validators": [ + "0xfdf3b0dfd4b822d10cacb15c8ae945ea269e7534" + ] }, "viction": { - "validators": ["0x1f87c368f8e05a85ef9126d984a980a20930cb9c"] + "validators": [ + "0x1f87c368f8e05a85ef9126d984a980a20930cb9c" + ] }, "worldchain": { - "validators": ["0x31048785845325b22817448b68d08f8a8fe36854"] + "validators": [ + "0x31048785845325b22817448b68d08f8a8fe36854" + ] }, "xai": { - "validators": ["0xe993f01fea86eb64cda45ae5af1d5be40ac0c7e9"] + "validators": [ + "0xe993f01fea86eb64cda45ae5af1d5be40ac0c7e9" + ] }, "xlayer": { - "validators": ["0xa2ae7c594703e988f23d97220717c513db638ea3"] + "validators": [ + "0xa2ae7c594703e988f23d97220717c513db638ea3" + ] }, "zeronetwork": { - "validators": ["0x1bd9e3f8a90ea1a13b0f2838a1858046368aad87"] + "validators": [ + "0x1bd9e3f8a90ea1a13b0f2838a1858046368aad87" + ] }, "zetachain": { - "validators": ["0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef"] + "validators": [ + "0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef" + ] }, "zircuit": { - "validators": ["0x169ec400cc758fef3df6a0d6c51fbc6cdd1015bb"] + "validators": [ + "0x169ec400cc758fef3df6a0d6c51fbc6cdd1015bb" + ] }, "zklink": { - "validators": ["0x217a8cb4789fc45abf56cb6e2ca96f251a5ac181"] + "validators": [ + "0x217a8cb4789fc45abf56cb6e2ca96f251a5ac181" + ] }, "zksync": { - "validators": ["0xadd1d39ce7a687e32255ac457cf99a6d8c5b5d1a"] + "validators": [ + "0xadd1d39ce7a687e32255ac457cf99a6d8c5b5d1a" + ] }, "zoramainnet": { - "validators": ["0x35130945b625bb69b28aee902a3b9a76fa67125f"] + "validators": [ + "0x35130945b625bb69b28aee902a3b9a76fa67125f" + ] } } diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json index 55638fa670..c5f4fc5bbe 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json @@ -1,6 +1,8 @@ { "ancient8": { - "validators": ["0xaae4d879a04e3d8b956eb4ffbefd57fdbed09cae"] + "validators": [ + "0xaae4d879a04e3d8b956eb4ffbefd57fdbed09cae" + ] }, "arbitrum": { "validators": [ @@ -10,10 +12,14 @@ ] }, "astar": { - "validators": ["0x50792eee9574f1bcd002e2c6dddf2aa73b04e254"] + "validators": [ + "0x50792eee9574f1bcd002e2c6dddf2aa73b04e254" + ] }, "astarzkevm": { - "validators": ["0x71e0a93f84548765cafc64d787eede19eea1ab06"] + "validators": [ + "0x71e0a93f84548765cafc64d787eede19eea1ab06" + ] }, "avalanche": { "validators": [ @@ -30,10 +36,14 @@ ] }, "bitlayer": { - "validators": ["0xec567167f07479bcd02a66c2ea66fd329cb9e22f"] + "validators": [ + "0xec567167f07479bcd02a66c2ea66fd329cb9e22f" + ] }, "blast": { - "validators": ["0x5b32f226e472da6ca19abfe1a29d5d28102a2d1a"] + "validators": [ + "0x5b32f226e472da6ca19abfe1a29d5d28102a2d1a" + ] }, "bsc": { "validators": [ @@ -50,22 +60,34 @@ ] }, "cheesechain": { - "validators": ["0xa481835355309ed46540c742a1c04b58380aa7b4"] + "validators": [ + "0xa481835355309ed46540c742a1c04b58380aa7b4" + ] }, "coredao": { - "validators": ["0x9767df5bcebce5ca89fdca5efbfb3b891cf30c5b"] + "validators": [ + "0x9767df5bcebce5ca89fdca5efbfb3b891cf30c5b" + ] }, "cyber": { - "validators": ["0x84976d5012da6b180462742f2edfae2f9d740c82"] + "validators": [ + "0x84976d5012da6b180462742f2edfae2f9d740c82" + ] }, "degenchain": { - "validators": ["0xac3734bb61e7ddda947b718b930c4067f0ccde7e"] + "validators": [ + "0xac3734bb61e7ddda947b718b930c4067f0ccde7e" + ] }, "dogechain": { - "validators": ["0x1e74735379a96586ec31f0a31aa4ee86016404a9"] + "validators": [ + "0x1e74735379a96586ec31f0a31aa4ee86016404a9" + ] }, "eclipsemainnet": { - "validators": ["0x283065cb17f98386c2b3656650d8b85e05862c8e"] + "validators": [ + "0x283065cb17f98386c2b3656650d8b85e05862c8e" + ] }, "ethereum": { "validators": [ @@ -75,13 +97,19 @@ ] }, "everclear": { - "validators": ["0x1b4455316aa859c9b3d2b13868490b7c97a9da3e"] + "validators": [ + "0x1b4455316aa859c9b3d2b13868490b7c97a9da3e" + ] }, "flare": { - "validators": ["0x35c3ce34f27def61c2d878ff7c3e35fbab08e92a"] + "validators": [ + "0x35c3ce34f27def61c2d878ff7c3e35fbab08e92a" + ] }, "fraxtal": { - "validators": ["0x8c772b730c8deb333dded14cb462e577a06283da"] + "validators": [ + "0x8c772b730c8deb333dded14cb462e577a06283da" + ] }, "gnosis": { "validators": [ @@ -98,16 +126,24 @@ ] }, "kroma": { - "validators": ["0x9b3142f04dd67585c9287f1aee42ff9754a955d3"] + "validators": [ + "0x9b3142f04dd67585c9287f1aee42ff9754a955d3" + ] }, "linea": { - "validators": ["0xad4886b6f5f5088c7ae53b69d1ff5cfc2a17bec4"] + "validators": [ + "0xad4886b6f5f5088c7ae53b69d1ff5cfc2a17bec4" + ] }, "lisk": { - "validators": ["0xa38e875985174ed2db358a2dea143e9df825880e"] + "validators": [ + "0xa38e875985174ed2db358a2dea143e9df825880e" + ] }, "lukso": { - "validators": ["0x0815a98af4285d82a65d4c006a8f100ea58f80cb"] + "validators": [ + "0x0815a98af4285d82a65d4c006a8f100ea58f80cb" + ] }, "mantapacific": { "validators": [ @@ -117,19 +153,29 @@ ] }, "merlin": { - "validators": ["0x93b08ce35b7676270b4aefda5e01c668cc61cb5c"] + "validators": [ + "0x93b08ce35b7676270b4aefda5e01c668cc61cb5c" + ] }, "metis": { - "validators": ["0xa01a62b1a6663e888399788ff15e784b5d223661"] + "validators": [ + "0xa01a62b1a6663e888399788ff15e784b5d223661" + ] }, "mint": { - "validators": ["0xbd8808980b59459cea0435d1d0c72a05e7660ff3"] + "validators": [ + "0xbd8808980b59459cea0435d1d0c72a05e7660ff3" + ] }, "mode": { - "validators": ["0x2f04ed30b1c27ef8e9e6acd360728d9bd5c3a9e2"] + "validators": [ + "0x2f04ed30b1c27ef8e9e6acd360728d9bd5c3a9e2" + ] }, "molten": { - "validators": ["0x2e433ffc514a874537ca2473af51ebba4c863409"] + "validators": [ + "0x2e433ffc514a874537ca2473af51ebba4c863409" + ] }, "moonbeam": { "validators": [ @@ -139,7 +185,9 @@ ] }, "oortmainnet": { - "validators": ["0x83f406ff315e90ae9589fa7786bf700e7c7a06f1"] + "validators": [ + "0x83f406ff315e90ae9589fa7786bf700e7c7a06f1" + ] }, "optimism": { "validators": [ @@ -163,16 +211,24 @@ ] }, "proofofplay": { - "validators": ["0xc18b802596b5d939c35c8e58afa5ef0872d3f792"] + "validators": [ + "0xc18b802596b5d939c35c8e58afa5ef0872d3f792" + ] }, "real": { - "validators": ["0x005c82fe794ef230153d22584f295d43dfc89438"] + "validators": [ + "0x005c82fe794ef230153d22584f295d43dfc89438" + ] }, "redstone": { - "validators": ["0x51ed7127c0afc0513a0f141e910c5e02b2a9a4b5"] + "validators": [ + "0x51ed7127c0afc0513a0f141e910c5e02b2a9a4b5" + ] }, "sanko": { - "validators": ["0xe12dcb5cab4a2979ac46c045fe7da3c93cf62808"] + "validators": [ + "0xe12dcb5cab4a2979ac46c045fe7da3c93cf62808" + ] }, "scroll": { "validators": [ @@ -182,16 +238,24 @@ ] }, "sei": { - "validators": ["0x846e48a7e85e5403cc690a347e1ad3c3dca11b6e"] + "validators": [ + "0x846e48a7e85e5403cc690a347e1ad3c3dca11b6e" + ] }, "shibarium": { - "validators": ["0x7f52b98a0a3e1ced83cf4eced8c0fa61886e7268"] + "validators": [ + "0x7f52b98a0a3e1ced83cf4eced8c0fa61886e7268" + ] }, "solanamainnet": { - "validators": ["0x7bd1536cb7505cf2ea1dc6744127d91fbe87a2ad"] + "validators": [ + "0x7bd1536cb7505cf2ea1dc6744127d91fbe87a2ad" + ] }, "tangle": { - "validators": ["0xd778d113975b2fe45eda424bf93c28f32a90b076"] + "validators": [ + "0xd778d113975b2fe45eda424bf93c28f32a90b076" + ] }, "viction": { "validators": [ @@ -201,16 +265,24 @@ ] }, "worldchain": { - "validators": ["0x385a2452930a0681d3ea4e40fb7722095142afcc"] + "validators": [ + "0x385a2452930a0681d3ea4e40fb7722095142afcc" + ] }, "xai": { - "validators": ["0x5c38bb0d73220755b5884978c9e7602552c30929"] + "validators": [ + "0x5c38bb0d73220755b5884978c9e7602552c30929" + ] }, "xlayer": { - "validators": ["0xa68e98cb98190485847581c8004b40ee81cbc723"] + "validators": [ + "0xa68e98cb98190485847581c8004b40ee81cbc723" + ] }, "zetachain": { - "validators": ["0xa13d146b47242671466e4041f5fe68d22a2ffe09"] + "validators": [ + "0xa13d146b47242671466e4041f5fe68d22a2ffe09" + ] }, "neutron": { "validators": [ From 43733b55cb561ee8899306723f6b208863f620bd Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 9 Dec 2024 18:50:42 -0700 Subject: [PATCH 14/33] pretty --- .../mainnet3/aw-validators/hyperlane.json | 324 +++++------------- .../mainnet3/aw-validators/rc.json | 144 ++------ 2 files changed, 117 insertions(+), 351 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json index c79aa3ea0d..9e39d900ce 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json @@ -1,23 +1,15 @@ { "ancient8": { - "validators": [ - "0xbb5842ae0e05215b53df4787a29144efb7e67551" - ] + "validators": ["0xbb5842ae0e05215b53df4787a29144efb7e67551"] }, "alephzeroevmmainnet": { - "validators": [ - "0x33f20e6e775747d60301c6ea1c50e51f0389740c" - ] + "validators": ["0x33f20e6e775747d60301c6ea1c50e51f0389740c"] }, "apechain": { - "validators": [ - "0x773d7fe6ffb1ba4de814c28044ff9a2d83a48221" - ] + "validators": ["0x773d7fe6ffb1ba4de814c28044ff9a2d83a48221"] }, "appchain": { - "validators": [ - "0x0531251bbadc1f9f19ccce3ca6b3f79f08eae1be" - ] + "validators": ["0x0531251bbadc1f9f19ccce3ca6b3f79f08eae1be"] }, "arbitrum": { "validators": [ @@ -27,24 +19,16 @@ ] }, "arbitrumnova": { - "validators": [ - "0xd2a5e9123308d187383c87053811a2c21bd8af1f" - ] + "validators": ["0xd2a5e9123308d187383c87053811a2c21bd8af1f"] }, "astar": { - "validators": [ - "0x4d1b2cade01ee3493f44304653d8e352c66ec3e7" - ] + "validators": ["0x4d1b2cade01ee3493f44304653d8e352c66ec3e7"] }, "astarzkevm": { - "validators": [ - "0x89ecdd6caf138934bf3a2fb7b323984d72fd66de" - ] + "validators": ["0x89ecdd6caf138934bf3a2fb7b323984d72fd66de"] }, "flame": { - "validators": [ - "0x1fa928ce884fa16357d4b8866e096392d4d81f43" - ] + "validators": ["0x1fa928ce884fa16357d4b8866e096392d4d81f43"] }, "avalanche": { "validators": [ @@ -54,9 +38,7 @@ ] }, "b3": { - "validators": [ - "0xd77b516730a836fc41934e7d5864e72c165b934e" - ] + "validators": ["0xd77b516730a836fc41934e7d5864e72c165b934e"] }, "base": { "validators": [ @@ -66,24 +48,16 @@ ] }, "bitlayer": { - "validators": [ - "0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f" - ] + "validators": ["0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f"] }, "blast": { - "validators": [ - "0xf20c0b09f597597c8d2430d3d72dfddaf09177d1" - ] + "validators": ["0xf20c0b09f597597c8d2430d3d72dfddaf09177d1"] }, "bob": { - "validators": [ - "0x20f283be1eb0e81e22f51705dcb79883cfdd34aa" - ] + "validators": ["0x20f283be1eb0e81e22f51705dcb79883cfdd34aa"] }, "boba": { - "validators": [ - "0xebeb92c94ca8408e73aa16fd554cb3a7df075c59" - ] + "validators": ["0xebeb92c94ca8408e73aa16fd554cb3a7df075c59"] }, "bsc": { "validators": [ @@ -93,9 +67,7 @@ ] }, "bsquared": { - "validators": [ - "0xcadc90933c9fbe843358a4e70e46ad2db78e28aa" - ] + "validators": ["0xcadc90933c9fbe843358a4e70e46ad2db78e28aa"] }, "celo": { "validators": [ @@ -105,49 +77,31 @@ ] }, "cheesechain": { - "validators": [ - "0x478fb53c6860ae8fc35235ba0d38d49b13128226" - ] + "validators": ["0x478fb53c6860ae8fc35235ba0d38d49b13128226"] }, "chilizmainnet": { - "validators": [ - "0x7403e5d58b48b0f5f715d9c78fbc581f01a625cb" - ] + "validators": ["0x7403e5d58b48b0f5f715d9c78fbc581f01a625cb"] }, "coredao": { - "validators": [ - "0xbd6e158a3f5830d99d7d2bce192695bc4a148de2" - ] + "validators": ["0xbd6e158a3f5830d99d7d2bce192695bc4a148de2"] }, "cyber": { - "validators": [ - "0x94d7119ceeb802173b6924e6cc8c4cd731089a27" - ] + "validators": ["0x94d7119ceeb802173b6924e6cc8c4cd731089a27"] }, "degenchain": { - "validators": [ - "0x433e311f19524cd64fb2123ad0aa1579a4e1fc83" - ] + "validators": ["0x433e311f19524cd64fb2123ad0aa1579a4e1fc83"] }, "dogechain": { - "validators": [ - "0xe43f742c37858746e6d7e458bc591180d0cba440" - ] + "validators": ["0xe43f742c37858746e6d7e458bc591180d0cba440"] }, "duckchain": { - "validators": [ - "0x91d55fe6dac596a6735d96365e21ce4bca21d83c" - ] + "validators": ["0x91d55fe6dac596a6735d96365e21ce4bca21d83c"] }, "eclipsemainnet": { - "validators": [ - "0xebb52d7eaa3ff7a5a6260bfe5111ce52d57401d0" - ] + "validators": ["0xebb52d7eaa3ff7a5a6260bfe5111ce52d57401d0"] }, "endurance": { - "validators": [ - "0x28c5b322da06f184ebf68693c5d19df4d4af13e5" - ] + "validators": ["0x28c5b322da06f184ebf68693c5d19df4d4af13e5"] }, "ethereum": { "validators": [ @@ -157,34 +111,22 @@ ] }, "everclear": { - "validators": [ - "0xeff20ae3d5ab90abb11e882cfce4b92ea6c74837" - ] + "validators": ["0xeff20ae3d5ab90abb11e882cfce4b92ea6c74837"] }, "fantom": { - "validators": [ - "0xa779572028e634e16f26af5dfd4fa685f619457d" - ] + "validators": ["0xa779572028e634e16f26af5dfd4fa685f619457d"] }, "flare": { - "validators": [ - "0xb65e52be342dba3ab2c088ceeb4290c744809134" - ] + "validators": ["0xb65e52be342dba3ab2c088ceeb4290c744809134"] }, "flowmainnet": { - "validators": [ - "0xe132235c958ca1f3f24d772e5970dd58da4c0f6e" - ] + "validators": ["0xe132235c958ca1f3f24d772e5970dd58da4c0f6e"] }, "fraxtal": { - "validators": [ - "0x4bce180dac6da60d0f3a2bdf036ffe9004f944c1" - ] + "validators": ["0x4bce180dac6da60d0f3a2bdf036ffe9004f944c1"] }, "fusemainnet": { - "validators": [ - "0x770c8ec9aac8cec4b2ead583b49acfbc5a1cf8a9" - ] + "validators": ["0x770c8ec9aac8cec4b2ead583b49acfbc5a1cf8a9"] }, "gnosis": { "validators": [ @@ -194,19 +136,13 @@ ] }, "gravity": { - "validators": [ - "0x23d549bf757a02a6f6068e9363196ecd958c974e" - ] + "validators": ["0x23d549bf757a02a6f6068e9363196ecd958c974e"] }, "harmony": { - "validators": [ - "0xd677803a67651974b1c264171b5d7ca8838db8d5" - ] + "validators": ["0xd677803a67651974b1c264171b5d7ca8838db8d5"] }, "immutablezkevmmainnet": { - "validators": [ - "0xbdda85b19a5efbe09e52a32db1a072f043dd66da" - ] + "validators": ["0xbdda85b19a5efbe09e52a32db1a072f043dd66da"] }, "inevm": { "validators": [ @@ -216,44 +152,28 @@ ] }, "injective": { - "validators": [ - "0xbfb8911b72cfb138c7ce517c57d9c691535dc517" - ] + "validators": ["0xbfb8911b72cfb138c7ce517c57d9c691535dc517"] }, "kaia": { - "validators": [ - "0x9de0b3abb221d19719882fa4d61f769fdc2be9a4" - ] + "validators": ["0x9de0b3abb221d19719882fa4d61f769fdc2be9a4"] }, "kroma": { - "validators": [ - "0x71b83c21342787d758199e4b8634d3a15f02dc6e" - ] + "validators": ["0x71b83c21342787d758199e4b8634d3a15f02dc6e"] }, "linea": { - "validators": [ - "0xf2d5409a59e0f5ae7635aff73685624904a77d94" - ] + "validators": ["0xf2d5409a59e0f5ae7635aff73685624904a77d94"] }, "lisk": { - "validators": [ - "0xc0b282aa5bac43fee83cf71dc3dd1797c1090ea5" - ] + "validators": ["0xc0b282aa5bac43fee83cf71dc3dd1797c1090ea5"] }, "lukso": { - "validators": [ - "0xa5e953701dcddc5b958b5defb677a829d908df6d" - ] + "validators": ["0xa5e953701dcddc5b958b5defb677a829d908df6d"] }, "lumia": { - "validators": [ - "0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f" - ] + "validators": ["0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f"] }, "lumiaprism": { - "validators": [ - "0xb69731640ffd4338a2c9358a935b0274c6463f85" - ] + "validators": ["0xb69731640ffd4338a2c9358a935b0274c6463f85"] }, "mantapacific": { "validators": [ @@ -263,39 +183,25 @@ ] }, "mantle": { - "validators": [ - "0xf930636c5a1a8bf9302405f72e3af3c96ebe4a52" - ] + "validators": ["0xf930636c5a1a8bf9302405f72e3af3c96ebe4a52"] }, "merlin": { - "validators": [ - "0xc1d6600cb9326ed2198cc8c4ba8d6668e8671247" - ] + "validators": ["0xc1d6600cb9326ed2198cc8c4ba8d6668e8671247"] }, "metal": { - "validators": [ - "0xd9f7f1a05826197a93df51e86cefb41dfbfb896a" - ] + "validators": ["0xd9f7f1a05826197a93df51e86cefb41dfbfb896a"] }, "metis": { - "validators": [ - "0xc4a3d25107060e800a43842964546db508092260" - ] + "validators": ["0xc4a3d25107060e800a43842964546db508092260"] }, "mint": { - "validators": [ - "0xfed01ccdd7a65e8a6ad867b7fb03b9eb47777ac9" - ] + "validators": ["0xfed01ccdd7a65e8a6ad867b7fb03b9eb47777ac9"] }, "mode": { - "validators": [ - "0x7eb2e1920a4166c19d6884c1cec3d2cf356fc9b7" - ] + "validators": ["0x7eb2e1920a4166c19d6884c1cec3d2cf356fc9b7"] }, "molten": { - "validators": [ - "0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f" - ] + "validators": ["0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f"] }, "moonbeam": { "validators": [ @@ -305,9 +211,7 @@ ] }, "morph": { - "validators": [ - "0x4884535f393151ec419add872100d352f71af380" - ] + "validators": ["0x4884535f393151ec419add872100d352f71af380"] }, "neutron": { "validators": [ @@ -317,9 +221,7 @@ ] }, "oortmainnet": { - "validators": [ - "0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7" - ] + "validators": ["0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7"] }, "optimism": { "validators": [ @@ -329,14 +231,10 @@ ] }, "orderly": { - "validators": [ - "0xec3dc91f9fa2ad35edf5842aa764d5573b778bb6" - ] + "validators": ["0xec3dc91f9fa2ad35edf5842aa764d5573b778bb6"] }, "osmosis": { - "validators": [ - "0xea483af11c19fa41b16c31d1534c2a486a92bcac" - ] + "validators": ["0xea483af11c19fa41b16c31d1534c2a486a92bcac"] }, "polygon": { "validators": [ @@ -353,44 +251,28 @@ ] }, "polynomialfi": { - "validators": [ - "0x23d348c2d365040e56f3fee07e6897122915f513" - ] + "validators": ["0x23d348c2d365040e56f3fee07e6897122915f513"] }, "prom": { - "validators": [ - "0xb0c4042b7c9a95345be8913f4cdbf4043b923d98" - ] + "validators": ["0xb0c4042b7c9a95345be8913f4cdbf4043b923d98"] }, "proofofplay": { - "validators": [ - "0xcda40baa71970a06e5f55e306474de5ca4e21c3b" - ] + "validators": ["0xcda40baa71970a06e5f55e306474de5ca4e21c3b"] }, "rarichain": { - "validators": [ - "0xeac012df7530720dd7d6f9b727e4fe39807d1516" - ] + "validators": ["0xeac012df7530720dd7d6f9b727e4fe39807d1516"] }, "real": { - "validators": [ - "0xaebadd4998c70b05ce8715cf0c3cb8862fe0beec" - ] + "validators": ["0xaebadd4998c70b05ce8715cf0c3cb8862fe0beec"] }, "redstone": { - "validators": [ - "0x1400b9737007f7978d8b4bbafb4a69c83f0641a7" - ] + "validators": ["0x1400b9737007f7978d8b4bbafb4a69c83f0641a7"] }, "rootstockmainnet": { - "validators": [ - "0x8675eb603d62ab64e3efe90df914e555966e04ac" - ] + "validators": ["0x8675eb603d62ab64e3efe90df914e555966e04ac"] }, "sanko": { - "validators": [ - "0x795c37d5babbc44094b084b0c89ed9db9b5fae39" - ] + "validators": ["0x795c37d5babbc44094b084b0c89ed9db9b5fae39"] }, "scroll": { "validators": [ @@ -400,113 +282,69 @@ ] }, "sei": { - "validators": [ - "0x9920d2dbf6c85ffc228fdc2e810bf895732c6aa5" - ] + "validators": ["0x9920d2dbf6c85ffc228fdc2e810bf895732c6aa5"] }, "shibarium": { - "validators": [ - "0xfa33391ee38597cbeef72ccde8c9e13e01e78521" - ] + "validators": ["0xfa33391ee38597cbeef72ccde8c9e13e01e78521"] }, "snaxchain": { - "validators": [ - "0x2c25829ae32a772d2a49f6c4b34f8b01fd03ef9e" - ] + "validators": ["0x2c25829ae32a772d2a49f6c4b34f8b01fd03ef9e"] }, "solanamainnet": { - "validators": [ - "0x28464752829b3ea59a497fca0bdff575c534c3ff" - ] + "validators": ["0x28464752829b3ea59a497fca0bdff575c534c3ff"] }, "superseed": { - "validators": [ - "0xdc2b87cb555411bb138d3a4e5f7832c87fae2b88" - ] + "validators": ["0xdc2b87cb555411bb138d3a4e5f7832c87fae2b88"] }, "superpositionmainnet": { - "validators": [ - "0x3f489acdd341c6b4dd86293fa2cc5ecc8ccf4f84" - ] + "validators": ["0x3f489acdd341c6b4dd86293fa2cc5ecc8ccf4f84"] }, "swell": { - "validators": [ - "0x4f51e4f4c7fb45d82f91568480a1a2cfb69216ed" - ] + "validators": ["0x4f51e4f4c7fb45d82f91568480a1a2cfb69216ed"] }, "taiko": { - "validators": [ - "0xa930073c8f2d0b2f7423ea32293e0d1362e65d79" - ] + "validators": ["0xa930073c8f2d0b2f7423ea32293e0d1362e65d79"] }, "tangle": { - "validators": [ - "0x1ee52cbbfacd7dcb0ba4e91efaa6fbc61602b15b" - ] + "validators": ["0x1ee52cbbfacd7dcb0ba4e91efaa6fbc61602b15b"] }, "treasure": { - "validators": [ - "0x6ad994819185553e8baa01533f0cd2c7cadfe6cc" - ] + "validators": ["0x6ad994819185553e8baa01533f0cd2c7cadfe6cc"] }, "unichain": { - "validators": [ - "0x9773a382342ebf604a2e5de0a1f462fb499e28b1" - ] + "validators": ["0x9773a382342ebf604a2e5de0a1f462fb499e28b1"] }, "vana": { - "validators": [ - "0xfdf3b0dfd4b822d10cacb15c8ae945ea269e7534" - ] + "validators": ["0xfdf3b0dfd4b822d10cacb15c8ae945ea269e7534"] }, "viction": { - "validators": [ - "0x1f87c368f8e05a85ef9126d984a980a20930cb9c" - ] + "validators": ["0x1f87c368f8e05a85ef9126d984a980a20930cb9c"] }, "worldchain": { - "validators": [ - "0x31048785845325b22817448b68d08f8a8fe36854" - ] + "validators": ["0x31048785845325b22817448b68d08f8a8fe36854"] }, "xai": { - "validators": [ - "0xe993f01fea86eb64cda45ae5af1d5be40ac0c7e9" - ] + "validators": ["0xe993f01fea86eb64cda45ae5af1d5be40ac0c7e9"] }, "xlayer": { - "validators": [ - "0xa2ae7c594703e988f23d97220717c513db638ea3" - ] + "validators": ["0xa2ae7c594703e988f23d97220717c513db638ea3"] }, "zeronetwork": { - "validators": [ - "0x1bd9e3f8a90ea1a13b0f2838a1858046368aad87" - ] + "validators": ["0x1bd9e3f8a90ea1a13b0f2838a1858046368aad87"] }, "zetachain": { - "validators": [ - "0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef" - ] + "validators": ["0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef"] }, "zircuit": { - "validators": [ - "0x169ec400cc758fef3df6a0d6c51fbc6cdd1015bb" - ] + "validators": ["0x169ec400cc758fef3df6a0d6c51fbc6cdd1015bb"] }, "zklink": { - "validators": [ - "0x217a8cb4789fc45abf56cb6e2ca96f251a5ac181" - ] + "validators": ["0x217a8cb4789fc45abf56cb6e2ca96f251a5ac181"] }, "zksync": { - "validators": [ - "0xadd1d39ce7a687e32255ac457cf99a6d8c5b5d1a" - ] + "validators": ["0xadd1d39ce7a687e32255ac457cf99a6d8c5b5d1a"] }, "zoramainnet": { - "validators": [ - "0x35130945b625bb69b28aee902a3b9a76fa67125f" - ] + "validators": ["0x35130945b625bb69b28aee902a3b9a76fa67125f"] } } diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json index c5f4fc5bbe..55638fa670 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json @@ -1,8 +1,6 @@ { "ancient8": { - "validators": [ - "0xaae4d879a04e3d8b956eb4ffbefd57fdbed09cae" - ] + "validators": ["0xaae4d879a04e3d8b956eb4ffbefd57fdbed09cae"] }, "arbitrum": { "validators": [ @@ -12,14 +10,10 @@ ] }, "astar": { - "validators": [ - "0x50792eee9574f1bcd002e2c6dddf2aa73b04e254" - ] + "validators": ["0x50792eee9574f1bcd002e2c6dddf2aa73b04e254"] }, "astarzkevm": { - "validators": [ - "0x71e0a93f84548765cafc64d787eede19eea1ab06" - ] + "validators": ["0x71e0a93f84548765cafc64d787eede19eea1ab06"] }, "avalanche": { "validators": [ @@ -36,14 +30,10 @@ ] }, "bitlayer": { - "validators": [ - "0xec567167f07479bcd02a66c2ea66fd329cb9e22f" - ] + "validators": ["0xec567167f07479bcd02a66c2ea66fd329cb9e22f"] }, "blast": { - "validators": [ - "0x5b32f226e472da6ca19abfe1a29d5d28102a2d1a" - ] + "validators": ["0x5b32f226e472da6ca19abfe1a29d5d28102a2d1a"] }, "bsc": { "validators": [ @@ -60,34 +50,22 @@ ] }, "cheesechain": { - "validators": [ - "0xa481835355309ed46540c742a1c04b58380aa7b4" - ] + "validators": ["0xa481835355309ed46540c742a1c04b58380aa7b4"] }, "coredao": { - "validators": [ - "0x9767df5bcebce5ca89fdca5efbfb3b891cf30c5b" - ] + "validators": ["0x9767df5bcebce5ca89fdca5efbfb3b891cf30c5b"] }, "cyber": { - "validators": [ - "0x84976d5012da6b180462742f2edfae2f9d740c82" - ] + "validators": ["0x84976d5012da6b180462742f2edfae2f9d740c82"] }, "degenchain": { - "validators": [ - "0xac3734bb61e7ddda947b718b930c4067f0ccde7e" - ] + "validators": ["0xac3734bb61e7ddda947b718b930c4067f0ccde7e"] }, "dogechain": { - "validators": [ - "0x1e74735379a96586ec31f0a31aa4ee86016404a9" - ] + "validators": ["0x1e74735379a96586ec31f0a31aa4ee86016404a9"] }, "eclipsemainnet": { - "validators": [ - "0x283065cb17f98386c2b3656650d8b85e05862c8e" - ] + "validators": ["0x283065cb17f98386c2b3656650d8b85e05862c8e"] }, "ethereum": { "validators": [ @@ -97,19 +75,13 @@ ] }, "everclear": { - "validators": [ - "0x1b4455316aa859c9b3d2b13868490b7c97a9da3e" - ] + "validators": ["0x1b4455316aa859c9b3d2b13868490b7c97a9da3e"] }, "flare": { - "validators": [ - "0x35c3ce34f27def61c2d878ff7c3e35fbab08e92a" - ] + "validators": ["0x35c3ce34f27def61c2d878ff7c3e35fbab08e92a"] }, "fraxtal": { - "validators": [ - "0x8c772b730c8deb333dded14cb462e577a06283da" - ] + "validators": ["0x8c772b730c8deb333dded14cb462e577a06283da"] }, "gnosis": { "validators": [ @@ -126,24 +98,16 @@ ] }, "kroma": { - "validators": [ - "0x9b3142f04dd67585c9287f1aee42ff9754a955d3" - ] + "validators": ["0x9b3142f04dd67585c9287f1aee42ff9754a955d3"] }, "linea": { - "validators": [ - "0xad4886b6f5f5088c7ae53b69d1ff5cfc2a17bec4" - ] + "validators": ["0xad4886b6f5f5088c7ae53b69d1ff5cfc2a17bec4"] }, "lisk": { - "validators": [ - "0xa38e875985174ed2db358a2dea143e9df825880e" - ] + "validators": ["0xa38e875985174ed2db358a2dea143e9df825880e"] }, "lukso": { - "validators": [ - "0x0815a98af4285d82a65d4c006a8f100ea58f80cb" - ] + "validators": ["0x0815a98af4285d82a65d4c006a8f100ea58f80cb"] }, "mantapacific": { "validators": [ @@ -153,29 +117,19 @@ ] }, "merlin": { - "validators": [ - "0x93b08ce35b7676270b4aefda5e01c668cc61cb5c" - ] + "validators": ["0x93b08ce35b7676270b4aefda5e01c668cc61cb5c"] }, "metis": { - "validators": [ - "0xa01a62b1a6663e888399788ff15e784b5d223661" - ] + "validators": ["0xa01a62b1a6663e888399788ff15e784b5d223661"] }, "mint": { - "validators": [ - "0xbd8808980b59459cea0435d1d0c72a05e7660ff3" - ] + "validators": ["0xbd8808980b59459cea0435d1d0c72a05e7660ff3"] }, "mode": { - "validators": [ - "0x2f04ed30b1c27ef8e9e6acd360728d9bd5c3a9e2" - ] + "validators": ["0x2f04ed30b1c27ef8e9e6acd360728d9bd5c3a9e2"] }, "molten": { - "validators": [ - "0x2e433ffc514a874537ca2473af51ebba4c863409" - ] + "validators": ["0x2e433ffc514a874537ca2473af51ebba4c863409"] }, "moonbeam": { "validators": [ @@ -185,9 +139,7 @@ ] }, "oortmainnet": { - "validators": [ - "0x83f406ff315e90ae9589fa7786bf700e7c7a06f1" - ] + "validators": ["0x83f406ff315e90ae9589fa7786bf700e7c7a06f1"] }, "optimism": { "validators": [ @@ -211,24 +163,16 @@ ] }, "proofofplay": { - "validators": [ - "0xc18b802596b5d939c35c8e58afa5ef0872d3f792" - ] + "validators": ["0xc18b802596b5d939c35c8e58afa5ef0872d3f792"] }, "real": { - "validators": [ - "0x005c82fe794ef230153d22584f295d43dfc89438" - ] + "validators": ["0x005c82fe794ef230153d22584f295d43dfc89438"] }, "redstone": { - "validators": [ - "0x51ed7127c0afc0513a0f141e910c5e02b2a9a4b5" - ] + "validators": ["0x51ed7127c0afc0513a0f141e910c5e02b2a9a4b5"] }, "sanko": { - "validators": [ - "0xe12dcb5cab4a2979ac46c045fe7da3c93cf62808" - ] + "validators": ["0xe12dcb5cab4a2979ac46c045fe7da3c93cf62808"] }, "scroll": { "validators": [ @@ -238,24 +182,16 @@ ] }, "sei": { - "validators": [ - "0x846e48a7e85e5403cc690a347e1ad3c3dca11b6e" - ] + "validators": ["0x846e48a7e85e5403cc690a347e1ad3c3dca11b6e"] }, "shibarium": { - "validators": [ - "0x7f52b98a0a3e1ced83cf4eced8c0fa61886e7268" - ] + "validators": ["0x7f52b98a0a3e1ced83cf4eced8c0fa61886e7268"] }, "solanamainnet": { - "validators": [ - "0x7bd1536cb7505cf2ea1dc6744127d91fbe87a2ad" - ] + "validators": ["0x7bd1536cb7505cf2ea1dc6744127d91fbe87a2ad"] }, "tangle": { - "validators": [ - "0xd778d113975b2fe45eda424bf93c28f32a90b076" - ] + "validators": ["0xd778d113975b2fe45eda424bf93c28f32a90b076"] }, "viction": { "validators": [ @@ -265,24 +201,16 @@ ] }, "worldchain": { - "validators": [ - "0x385a2452930a0681d3ea4e40fb7722095142afcc" - ] + "validators": ["0x385a2452930a0681d3ea4e40fb7722095142afcc"] }, "xai": { - "validators": [ - "0x5c38bb0d73220755b5884978c9e7602552c30929" - ] + "validators": ["0x5c38bb0d73220755b5884978c9e7602552c30929"] }, "xlayer": { - "validators": [ - "0xa68e98cb98190485847581c8004b40ee81cbc723" - ] + "validators": ["0xa68e98cb98190485847581c8004b40ee81cbc723"] }, "zetachain": { - "validators": [ - "0xa13d146b47242671466e4041f5fe68d22a2ffe09" - ] + "validators": ["0xa13d146b47242671466e4041f5fe68d22a2ffe09"] }, "neutron": { "validators": [ From 9df2c05f833ad03e024766e51d4595ab685e0e4b Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 9 Dec 2024 19:25:09 -0700 Subject: [PATCH 15/33] nit --- .../chains/hyperlane-sealevel/src/mailbox.rs | 37 ++++++++++--------- .../config/environments/mainnet3/agent.ts | 4 +- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index bd03803398..ee0ca003b8 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -374,25 +374,28 @@ impl SealevelMailbox { compute_unit_limit, )); + let prospective_tip: u64 = compute_unit_price_micro_lamports * compute_unit_limit as u64; + tracing::warn!(prospective_tip, "Prospective tip"); + // If we're using Jito, we need to send a tip to the Jito fee account. // Otherwise, we need to set the compute unit price. - if self.is_solana() { - let tip: u64 = compute_unit_price_micro_lamports * compute_unit_limit as u64; - - // The tip is a standalone transfer to a Jito fee account. - // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. - instructions.push(solana_sdk::system_instruction::transfer( - &payer.pubkey(), - // A random Jito fee account, taken from the getFeeAccount RPC response: - // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts - &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), - tip, - )); - } else { - instructions.push(ComputeBudgetInstruction::set_compute_unit_price( - compute_unit_price_micro_lamports, - )); - } + // if self.is_solana() { + // let tip: u64 = compute_unit_price_micro_lamports * compute_unit_limit as u64; + + // // The tip is a standalone transfer to a Jito fee account. + // // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. + // instructions.push(solana_sdk::system_instruction::transfer( + // &payer.pubkey(), + // // A random Jito fee account, taken from the getFeeAccount RPC response: + // // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts + // &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), + // tip, + // )); + // } else { + instructions.push(ComputeBudgetInstruction::set_compute_unit_price( + compute_unit_price_micro_lamports, + )); + // } instructions.push(instruction); diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index ad9a3f85ac..dbde870360 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -369,7 +369,7 @@ const sealevelPriorityFeeOracleConfigGetter = ( if (chain === 'solanamainnet') { return { type: AgentSealevelPriorityFeeOracleType.Helius, - feeLevel: AgentSealevelHeliusFeeLevel.High, + feeLevel: AgentSealevelHeliusFeeLevel.Medium, // URL is populated by the external secrets in the helm chart url: '', }; @@ -529,7 +529,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'b12bb5c-20241207-054843', + tag: '43733b5-20241210-015124', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. From fd48da8614e73571fef57bc15f2cd40e17c6ece4 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 9 Dec 2024 19:26:30 -0700 Subject: [PATCH 16/33] micro lamports, oopsie --- rust/main/chains/hyperlane-sealevel/src/mailbox.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index ee0ca003b8..2d76dfd499 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -380,7 +380,7 @@ impl SealevelMailbox { // If we're using Jito, we need to send a tip to the Jito fee account. // Otherwise, we need to set the compute unit price. // if self.is_solana() { - // let tip: u64 = compute_unit_price_micro_lamports * compute_unit_limit as u64; + // let tip: u64 = (compute_unit_price_micro_lamports * compute_unit_limit as u64) / 1e6; // // The tip is a standalone transfer to a Jito fee account. // // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. From 172da22d1dc5ab6329c718b03af3d11ced9d7d37 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 9 Dec 2024 20:16:10 -0700 Subject: [PATCH 17/33] better --- .../main/chains/hyperlane-sealevel/src/mailbox.rs | 8 ++------ .../chains/hyperlane-sealevel/src/rpc/client.rs | 15 +++++++++++---- .../infra/config/environments/mainnet3/agent.ts | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index 2d76dfd499..35373d1ffa 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -521,18 +521,14 @@ impl SealevelMailbox { if self.is_solana() { if let PriorityFeeOracleConfig::Helius(helius) = &self.priority_fee_oracle_config { let rpc = SealevelRpcClient::new(helius.url.clone().into()); - return self - .provider - .rpc() - .send_and_confirm_transaction(transaction) - .await; + return rpc.send_transaction(transaction, true).await; } else { tracing::warn!("Priority fee oracle is not Helius, falling back to normal RPC"); } } self.provider .rpc() - .send_and_confirm_transaction(transaction) + .send_transaction(transaction, true) .await } diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 8e5ee3920e..315557c176 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -4,8 +4,8 @@ use serializable_account_meta::{SerializableAccountMeta, SimulationReturnData}; use solana_client::{ nonblocking::rpc_client::RpcClient, rpc_config::{ - RpcBlockConfig, RpcProgramAccountsConfig, RpcSimulateTransactionConfig, - RpcTransactionConfig, + RpcBlockConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig, + RpcSimulateTransactionConfig, RpcTransactionConfig, }, rpc_response::{Response, RpcSimulateTransactionResult}, }; @@ -203,12 +203,19 @@ impl SealevelRpcClient { .map_err(ChainCommunicationError::from_other) } - pub async fn send_and_confirm_transaction( + pub async fn send_transaction( &self, transaction: &Transaction, + skip_preflight: bool, ) -> ChainResult { self.0 - .send_and_confirm_transaction(transaction) + .send_transaction_with_config( + transaction, + RpcSendTransactionConfig { + skip_preflight, + ..Default::default() + }, + ) .await .map_err(ChainCommunicationError::from_other) } diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index dbde870360..f424625011 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -529,7 +529,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '43733b5-20241210-015124', + tag: '9df2c05-20241210-022553', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. From 2a021dc28199e2fc6ce89c35e45c67ca193c66fb Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Tue, 10 Dec 2024 08:11:32 -0700 Subject: [PATCH 18/33] update image --- typescript/infra/config/environments/mainnet3/agent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index f424625011..e232405fd2 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -529,7 +529,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '9df2c05-20241210-022553', + tag: '172da22-20241210-031655', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. From ba3efa10521a9a94b299e48b3374b2ebe2aac5cd Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Tue, 10 Dec 2024 18:05:35 -0700 Subject: [PATCH 19/33] try with jito again --- .../chains/hyperlane-sealevel/src/mailbox.rs | 148 +++++++++--------- 1 file changed, 75 insertions(+), 73 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index 35373d1ffa..aa5c00a2d7 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -379,23 +379,25 @@ impl SealevelMailbox { // If we're using Jito, we need to send a tip to the Jito fee account. // Otherwise, we need to set the compute unit price. - // if self.is_solana() { - // let tip: u64 = (compute_unit_price_micro_lamports * compute_unit_limit as u64) / 1e6; - - // // The tip is a standalone transfer to a Jito fee account. - // // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. - // instructions.push(solana_sdk::system_instruction::transfer( - // &payer.pubkey(), - // // A random Jito fee account, taken from the getFeeAccount RPC response: - // // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts - // &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), - // tip, - // )); - // } else { - instructions.push(ComputeBudgetInstruction::set_compute_unit_price( - compute_unit_price_micro_lamports, - )); - // } + if self.is_solana() { + // Tip in lamports + let tip: u64 = + (compute_unit_price_micro_lamports * compute_unit_limit as u64) / 1e6 as u64; + + // The tip is a standalone transfer to a Jito fee account. + // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. + instructions.push(solana_sdk::system_instruction::transfer( + &payer.pubkey(), + // A random Jito fee account, taken from the getFeeAccount RPC response: + // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts + &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), + tip, + )); + } else { + instructions.push(ComputeBudgetInstruction::set_compute_unit_price( + compute_unit_price_micro_lamports, + )); + } instructions.push(instruction); @@ -508,28 +510,28 @@ impl SealevelMailbox { &self, transaction: &Transaction, ) -> ChainResult { + if self.is_solana() { + self.send_and_confirm_transaction_with_jito(transaction) + .await + } else { + self.provider + .rpc() + .send_transaction(transaction, true) + .await + } + // if self.is_solana() { - // self.send_and_confirm_transaction_with_jito(transaction) - // .await - // } else { + // if let PriorityFeeOracleConfig::Helius(helius) = &self.priority_fee_oracle_config { + // let rpc = SealevelRpcClient::new(helius.url.clone().into()); + // return rpc.send_transaction(transaction, true).await; + // } else { + // tracing::warn!("Priority fee oracle is not Helius, falling back to normal RPC"); + // } + // } // self.provider // .rpc() - // .send_and_confirm_transaction(transaction) + // .send_transaction(transaction, true) // .await - // } - - if self.is_solana() { - if let PriorityFeeOracleConfig::Helius(helius) = &self.priority_fee_oracle_config { - let rpc = SealevelRpcClient::new(helius.url.clone().into()); - return rpc.send_transaction(transaction, true).await; - } else { - tracing::warn!("Priority fee oracle is not Helius, falling back to normal RPC"); - } - } - self.provider - .rpc() - .send_transaction(transaction, true) - .await } // Stolen from Solana's non-blocking client, but with Jito! @@ -577,44 +579,44 @@ impl SealevelMailbox { "Got Jito response for sealevel transaction bundle" ); - let recent_blockhash = if transaction.uses_durable_nonce() { - self.provider - .rpc() - .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) - .await? - } else { - *transaction.get_recent_blockhash() - }; - - for status_retry in 0..GET_STATUS_RETRIES { - let signature_statuses: Response>> = self - .provider - .rpc() - .get_signature_statuses(&[*signature]) - .await?; - let signature_status = signature_statuses.value.first().cloned().flatten(); - match signature_status { - Some(_) => return Ok(*signature), - None => { - if !self - .provider - .rpc() - .is_blockhash_valid(&recent_blockhash) - .await? - { - // Block hash is not found by some reason - break 'sending; - } else if cfg!(not(test)) - // Ignore sleep at last step. - && status_retry < GET_STATUS_RETRIES - { - // Retry twice a second - tokio::time::sleep(std::time::Duration::from_millis(500)).await; - continue; - } - } - } - } + // let recent_blockhash = if transaction.uses_durable_nonce() { + // self.provider + // .rpc() + // .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) + // .await? + // } else { + // *transaction.get_recent_blockhash() + // }; + + // for status_retry in 0..GET_STATUS_RETRIES { + // let signature_statuses: Response>> = self + // .provider + // .rpc() + // .get_signature_statuses(&[*signature]) + // .await?; + // let signature_status = signature_statuses.value.first().cloned().flatten(); + // match signature_status { + // Some(_) => return Ok(*signature), + // None => { + // if !self + // .provider + // .rpc() + // .is_blockhash_valid(&recent_blockhash) + // .await? + // { + // // Block hash is not found by some reason + // break 'sending; + // } else if cfg!(not(test)) + // // Ignore sleep at last step. + // && status_retry < GET_STATUS_RETRIES + // { + // // Retry twice a second + // tokio::time::sleep(std::time::Duration::from_millis(500)).await; + // continue; + // } + // } + // } + // } } Err(ChainCommunicationError::from_other( From e0d8da2f506aa7f12528b0077bc02f81032c4ea2 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Tue, 10 Dec 2024 21:39:38 -0700 Subject: [PATCH 20/33] add tx submitter --- .../main/chains/hyperlane-sealevel/src/lib.rs | 1 + .../chains/hyperlane-sealevel/src/mailbox.rs | 295 +++++++++--------- .../hyperlane-sealevel/src/rpc/client.rs | 4 + .../hyperlane-sealevel/src/trait_builder.rs | 48 ++- .../hyperlane-sealevel/src/tx_submitter.rs | 115 +++++++ .../src/settings/parser/connection_parser.rs | 176 +++++++---- 6 files changed, 420 insertions(+), 219 deletions(-) create mode 100644 rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs diff --git a/rust/main/chains/hyperlane-sealevel/src/lib.rs b/rust/main/chains/hyperlane-sealevel/src/lib.rs index 05db6a7d93..6121bbe27f 100644 --- a/rust/main/chains/hyperlane-sealevel/src/lib.rs +++ b/rust/main/chains/hyperlane-sealevel/src/lib.rs @@ -27,5 +27,6 @@ mod priority_fee; mod provider; mod rpc; mod trait_builder; +mod tx_submitter; mod utils; mod validator_announce; diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index aa5c00a2d7..1c84a80a23 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -61,7 +61,6 @@ use hyperlane_core::{ TxCostEstimate, TxOutcome, H256, H512, U256, }; -use crate::error::HyperlaneSealevelError; use crate::log_meta_composer::{ is_interchain_payment_instruction, is_message_delivery_instruction, is_message_dispatch_instruction, LogMetaComposer, @@ -72,6 +71,7 @@ use crate::{ priority_fee::PriorityFeeOracle, PriorityFeeOracleConfig, }; +use crate::{error::HyperlaneSealevelError, tx_submitter::TransactionSubmitter}; use crate::{ConnectionConf, SealevelProvider, SealevelRpcClient}; const SYSTEM_PROGRAM: &str = "11111111111111111111111111111111"; @@ -111,6 +111,7 @@ pub struct SealevelMailbox { payer: Option, priority_fee_oracle: Box, priority_fee_oracle_config: PriorityFeeOracleConfig, + tx_submitter: Box, } impl SealevelMailbox { @@ -135,10 +136,13 @@ impl SealevelMailbox { program_id, inbox, outbox, - provider, payer, priority_fee_oracle: conf.priority_fee_oracle.create_oracle(), priority_fee_oracle_config: conf.priority_fee_oracle.clone(), + tx_submitter: conf + .transaction_submitter + .create_submitter(provider.rpc().url()), + provider, }) } @@ -368,38 +372,17 @@ impl SealevelMailbox { ) -> ChainResult { let payer = self.get_payer()?; - let mut instructions = Vec::with_capacity(3); - // Set the compute unit limit. - instructions.push(ComputeBudgetInstruction::set_compute_unit_limit( - compute_unit_limit, - )); - - let prospective_tip: u64 = compute_unit_price_micro_lamports * compute_unit_limit as u64; - tracing::warn!(prospective_tip, "Prospective tip"); - - // If we're using Jito, we need to send a tip to the Jito fee account. - // Otherwise, we need to set the compute unit price. - if self.is_solana() { - // Tip in lamports - let tip: u64 = - (compute_unit_price_micro_lamports * compute_unit_limit as u64) / 1e6 as u64; - - // The tip is a standalone transfer to a Jito fee account. - // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. - instructions.push(solana_sdk::system_instruction::transfer( - &payer.pubkey(), - // A random Jito fee account, taken from the getFeeAccount RPC response: - // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts - &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), - tip, - )); - } else { - instructions.push(ComputeBudgetInstruction::set_compute_unit_price( + let instructions = vec![ + // Set the compute unit limit. + ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit), + // Set the priority fee / tip + self.tx_submitter.get_priority_fee_instruction( compute_unit_price_micro_lamports, - )); - } - - instructions.push(instruction); + compute_unit_limit.into(), + &payer.pubkey(), + ), + instruction, + ]; let tx = if sign { let recent_blockhash = self @@ -506,128 +489,128 @@ impl SealevelMailbox { Ok(process_instruction) } - async fn send_and_confirm_transaction( - &self, - transaction: &Transaction, - ) -> ChainResult { - if self.is_solana() { - self.send_and_confirm_transaction_with_jito(transaction) - .await - } else { - self.provider - .rpc() - .send_transaction(transaction, true) - .await - } - - // if self.is_solana() { - // if let PriorityFeeOracleConfig::Helius(helius) = &self.priority_fee_oracle_config { - // let rpc = SealevelRpcClient::new(helius.url.clone().into()); - // return rpc.send_transaction(transaction, true).await; - // } else { - // tracing::warn!("Priority fee oracle is not Helius, falling back to normal RPC"); - // } - // } - // self.provider - // .rpc() - // .send_transaction(transaction, true) - // .await - } - - // Stolen from Solana's non-blocking client, but with Jito! - pub async fn send_and_confirm_transaction_with_jito( - &self, - transaction: &impl SerializableTransaction, - ) -> ChainResult { - let signature = transaction.get_signature(); - - let base58_txn = bs58::encode( - bincode::serialize(&transaction).map_err(ChainCommunicationError::from_other)?, - ) - .into_string(); - - const SEND_RETRIES: usize = 1; - const GET_STATUS_RETRIES: usize = usize::MAX; - - 'sending: for _ in 0..SEND_RETRIES { - let jito_request_body = serde_json::json!({ - "jsonrpc": "2.0", - "id": 1, - "method": "sendBundle", - "params": [ - [base58_txn] - ], - }); - - tracing::info!( - ?jito_request_body, - ?signature, - "Sending sealevel transaction to Jito as bundle" - ); - - let jito_response = reqwest::Client::new() - .post("https://mainnet.block-engine.jito.wtf:443/api/v1/bundles") - .json(&jito_request_body) - .send() - .await - .map_err(ChainCommunicationError::from_other)?; - let jito_response_text = jito_response.text().await; - - tracing::info!( - ?signature, - ?jito_response_text, - "Got Jito response for sealevel transaction bundle" - ); - - // let recent_blockhash = if transaction.uses_durable_nonce() { - // self.provider - // .rpc() - // .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) - // .await? - // } else { - // *transaction.get_recent_blockhash() - // }; - - // for status_retry in 0..GET_STATUS_RETRIES { - // let signature_statuses: Response>> = self - // .provider - // .rpc() - // .get_signature_statuses(&[*signature]) - // .await?; - // let signature_status = signature_statuses.value.first().cloned().flatten(); - // match signature_status { - // Some(_) => return Ok(*signature), - // None => { - // if !self - // .provider - // .rpc() - // .is_blockhash_valid(&recent_blockhash) - // .await? - // { - // // Block hash is not found by some reason - // break 'sending; - // } else if cfg!(not(test)) - // // Ignore sleep at last step. - // && status_retry < GET_STATUS_RETRIES - // { - // // Retry twice a second - // tokio::time::sleep(std::time::Duration::from_millis(500)).await; - // continue; - // } - // } - // } - // } - } - - Err(ChainCommunicationError::from_other( - solana_client::rpc_request::RpcError::ForUser( - "unable to confirm transaction. \ - This can happen in situations such as transaction expiration \ - and insufficient fee-payer funds" - .to_string(), - ), - )) - } + // async fn send_and_confirm_transaction( + // &self, + // transaction: &Transaction, + // ) -> ChainResult { + // if self.is_solana() { + // self.send_and_confirm_transaction_with_jito(transaction) + // .await + // } else { + // self.provider + // .rpc() + // .send_transaction(transaction, true) + // .await + // } + + // // if self.is_solana() { + // // if let PriorityFeeOracleConfig::Helius(helius) = &self.priority_fee_oracle_config { + // // let rpc = SealevelRpcClient::new(helius.url.clone().into()); + // // return rpc.send_transaction(transaction, true).await; + // // } else { + // // tracing::warn!("Priority fee oracle is not Helius, falling back to normal RPC"); + // // } + // // } + // // self.provider + // // .rpc() + // // .send_transaction(transaction, true) + // // .await + // } + + // // Stolen from Solana's non-blocking client, but with Jito! + // pub async fn send_and_confirm_transaction_with_jito( + // &self, + // transaction: &impl SerializableTransaction, + // ) -> ChainResult { + // let signature = transaction.get_signature(); + + // let base58_txn = bs58::encode( + // bincode::serialize(&transaction).map_err(ChainCommunicationError::from_other)?, + // ) + // .into_string(); + + // const SEND_RETRIES: usize = 1; + // const GET_STATUS_RETRIES: usize = usize::MAX; + + // 'sending: for _ in 0..SEND_RETRIES { + // let jito_request_body = serde_json::json!({ + // "jsonrpc": "2.0", + // "id": 1, + // "method": "sendBundle", + // "params": [ + // [base58_txn] + // ], + // }); + + // tracing::info!( + // ?jito_request_body, + // ?signature, + // "Sending sealevel transaction to Jito as bundle" + // ); + + // let jito_response = reqwest::Client::new() + // .post("https://mainnet.block-engine.jito.wtf:443/api/v1/bundles") + // .json(&jito_request_body) + // .send() + // .await + // .map_err(ChainCommunicationError::from_other)?; + // let jito_response_text = jito_response.text().await; + + // tracing::info!( + // ?signature, + // ?jito_response_text, + // "Got Jito response for sealevel transaction bundle" + // ); + + // // let recent_blockhash = if transaction.uses_durable_nonce() { + // // self.provider + // // .rpc() + // // .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) + // // .await? + // // } else { + // // *transaction.get_recent_blockhash() + // // }; + + // // for status_retry in 0..GET_STATUS_RETRIES { + // // let signature_statuses: Response>> = self + // // .provider + // // .rpc() + // // .get_signature_statuses(&[*signature]) + // // .await?; + // // let signature_status = signature_statuses.value.first().cloned().flatten(); + // // match signature_status { + // // Some(_) => return Ok(*signature), + // // None => { + // // if !self + // // .provider + // // .rpc() + // // .is_blockhash_valid(&recent_blockhash) + // // .await? + // // { + // // // Block hash is not found by some reason + // // break 'sending; + // // } else if cfg!(not(test)) + // // // Ignore sleep at last step. + // // && status_retry < GET_STATUS_RETRIES + // // { + // // // Retry twice a second + // // tokio::time::sleep(std::time::Duration::from_millis(500)).await; + // // continue; + // // } + // // } + // // } + // // } + // } + + // Err(ChainCommunicationError::from_other( + // solana_client::rpc_request::RpcError::ForUser( + // "unable to confirm transaction. \ + // This can happen in situations such as transaction expiration \ + // and insufficient fee-payer funds" + // .to_string(), + // ), + // )) + // } async fn get_inbox(&self) -> ChainResult> { let account = self @@ -740,7 +723,7 @@ impl Mailbox for SealevelMailbox { tracing::info!(?tx, "Created sealevel transaction to process message"); - let signature = self.send_and_confirm_transaction(&tx).await?; + let signature = self.tx_submitter.send_transaction(&tx, true).await?; tracing::info!(?tx, ?signature, "Sealevel transaction sent"); diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 315557c176..557a37a9e5 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -276,6 +276,10 @@ impl SealevelRpcClient { Ok(result) } + + pub fn url(&self) -> String { + self.0.url() + } } impl std::fmt::Debug for SealevelRpcClient { diff --git a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs index 4a3ee86efe..ea3ec914d5 100644 --- a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs +++ b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs @@ -1,7 +1,10 @@ use hyperlane_core::{config::OperationBatchConfig, ChainCommunicationError, NativeToken}; use url::Url; -use crate::priority_fee::{ConstantPriorityFeeOracle, HeliusPriorityFeeOracle, PriorityFeeOracle}; +use crate::{ + priority_fee::{ConstantPriorityFeeOracle, HeliusPriorityFeeOracle, PriorityFeeOracle}, + tx_submitter::{JitoTransactionSubmitter, RpcTransactionSubmitter, TransactionSubmitter}, +}; /// Sealevel connection configuration #[derive(Debug, Clone)] @@ -14,6 +17,8 @@ pub struct ConnectionConf { pub native_token: NativeToken, /// Priority fee oracle configuration pub priority_fee_oracle: PriorityFeeOracleConfig, + /// Transaction submitter configuration + pub transaction_submitter: TransactionSubmitterConfig, } /// An error type when parsing a connection configuration. @@ -82,6 +87,47 @@ pub enum HeliusPriorityFeeLevel { UnsafeMax, } +/// Configuration for the transaction submitter +#[derive(Debug, Clone)] +pub enum TransactionSubmitterConfig { + /// Use the RPC transaction submitter + Rpc { + /// The URL to use. If not provided, a default RPC URL will be used + url: Option, + }, + /// Use the Jito transaction submitter + Jito { + /// The URL to use. If not provided, a default Jito URL will be used + url: Option, + }, +} + +impl Default for TransactionSubmitterConfig { + fn default() -> Self { + TransactionSubmitterConfig::Rpc { url: None } + } +} + +impl TransactionSubmitterConfig { + /// Create a new transaction submitter from the configuration + pub fn create_submitter(&self, default_rpc_url: String) -> Box { + match self { + TransactionSubmitterConfig::Rpc { url } => Box::new(RpcTransactionSubmitter::new( + url.clone().unwrap_or(default_rpc_url), + )), + TransactionSubmitterConfig::Jito { url } => { + // Default to a bundle-only URL (i.e. revert protected) + Box::new(JitoTransactionSubmitter::new(url.clone().unwrap_or_else( + || { + "https://mainnet.block-engine.jito.wtf/api/v1/transactions?bundleOnly=true" + .to_string() + }, + ))) + } + } + } +} + #[derive(thiserror::Error, Debug)] #[error(transparent)] struct SealevelNewConnectionError(#[from] anyhow::Error); diff --git a/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs b/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs new file mode 100644 index 0000000000..6ac5643bb6 --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs @@ -0,0 +1,115 @@ +#![allow(dead_code)] + +use async_trait::async_trait; +use hyperlane_core::ChainResult; +use solana_sdk::{ + compute_budget::ComputeBudgetInstruction, instruction::Instruction, pubkey::Pubkey, + signature::Signature, transaction::Transaction, +}; + +use crate::SealevelRpcClient; + +#[async_trait] +pub trait TransactionSubmitter: Send + Sync { + fn get_priority_fee_instruction( + &self, + compute_unit_price_micro_lamports: u64, + compute_units: u64, + payer: &Pubkey, + ) -> Instruction; + + async fn send_transaction( + &self, + transaction: &Transaction, + skip_preflight: bool, + ) -> ChainResult; +} + +#[derive(Debug)] +pub struct RpcTransactionSubmitter { + rpc_client: SealevelRpcClient, +} + +impl RpcTransactionSubmitter { + pub fn new(url: String) -> Self { + Self { + rpc_client: SealevelRpcClient::new(url), + } + } + + pub fn new_with_rpc_client(rpc_client: SealevelRpcClient) -> Self { + Self { rpc_client } + } +} + +#[async_trait] +impl TransactionSubmitter for RpcTransactionSubmitter { + fn get_priority_fee_instruction( + &self, + compute_unit_price_micro_lamports: u64, + _compute_units: u64, + _payer: &Pubkey, + ) -> Instruction { + ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price_micro_lamports) + } + + async fn send_transaction( + &self, + transaction: &Transaction, + skip_preflight: bool, + ) -> ChainResult { + self.rpc_client + .send_transaction(transaction, skip_preflight) + .await + } +} + +#[derive(Debug)] +pub struct JitoTransactionSubmitter { + rpc_client: SealevelRpcClient, +} + +impl JitoTransactionSubmitter { + // From https://docs.jito.wtf/lowlatencytxnsend/#sendtransaction + const MINIMUM_TIP_LAMPORTS: u64 = 1000; + + pub fn new(url: String) -> Self { + Self { + rpc_client: SealevelRpcClient::new(url), + } + } +} + +#[async_trait] +impl TransactionSubmitter for JitoTransactionSubmitter { + fn get_priority_fee_instruction( + &self, + compute_unit_price_micro_lamports: u64, + compute_units: u64, + payer: &Pubkey, + ) -> Instruction { + // Divide by 1_000_000 to convert from microlamports to lamports. + let tip_lamports = (compute_units * compute_unit_price_micro_lamports) / 1_000_000; + let tip_lamports = tip_lamports.max(Self::MINIMUM_TIP_LAMPORTS); + + // The tip is a standalone transfer to a Jito fee account. + // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. + solana_sdk::system_instruction::transfer( + payer, + // A random Jito fee account, taken from the getFeeAccount RPC response: + // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts + &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), + tip_lamports, + ) + } + + async fn send_transaction( + &self, + transaction: &Transaction, + skip_preflight: bool, + ) -> ChainResult { + self.rpc_client + .send_transaction(transaction, skip_preflight) + .await + } +} diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index a269f99e5a..2375464415 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -165,13 +165,24 @@ fn build_sealevel_connection_conf( chain: &ValueParser, err: &mut ConfigParsingError, operation_batch: OperationBatchConfig, -) -> h_sealevel::ConnectionConf { +) -> Option { + let mut local_err = ConfigParsingError::default(); + let native_token = parse_native_token(chain, err, 9); - h_sealevel::ConnectionConf { - url: url.clone(), - operation_batch, - native_token, - priority_fee_oracle: parse_priority_fee_oracle_config(chain, err), + let priority_fee_oracle = parse_priority_fee_oracle_config(chain, &mut local_err); + let transaction_submitter = parse_transaction_submitter_config(chain, &mut local_err); + + if !local_err.is_ok() { + err.merge(local_err); + None + } else { + Some(ChainConnectionConf::Sealevel(h_sealevel::ConnectionConf { + url: url.clone(), + operation_batch, + native_token, + priority_fee_oracle: priority_fee_oracle.unwrap(), + transaction_submitter: transaction_submitter.unwrap(), + })) } } @@ -203,58 +214,58 @@ fn parse_native_token( fn parse_priority_fee_oracle_config( chain: &ValueParser, err: &mut ConfigParsingError, -) -> PriorityFeeOracleConfig { - let priority_fee_oracle = chain - .chain(err) - .get_opt_key("priorityFeeOracle") - .end() - .map(|value_parser| { - let oracle_type = value_parser - .chain(err) - .get_key("type") - .parse_string() - .end() - .or_else(|| { - err.push( - &value_parser.cwp + "type", - eyre!("Missing priority fee oracle type"), - ); - None - }) - .unwrap_or_default(); +) -> Option { + let value_parser = chain.chain(err).get_opt_key("priorityFeeOracle").end(); + + let priority_fee_oracle = if let Some(value_parser) = value_parser { + let oracle_type = value_parser + .chain(err) + .get_key("type") + .parse_string() + .end() + .or_else(|| { + err.push( + &value_parser.cwp + "type", + eyre!("Missing priority fee oracle type"), + ); + None + }) + .unwrap_or_default(); - match oracle_type { - "constant" => { - let fee = value_parser + match oracle_type { + "constant" => { + let fee = value_parser + .chain(err) + .get_key("fee") + .parse_u64() + .end() + .unwrap_or(0); + Some(PriorityFeeOracleConfig::Constant(fee)) + } + "helius" => { + let config = HeliusPriorityFeeOracleConfig { + url: value_parser .chain(err) - .get_key("fee") - .parse_u64() + .get_key("url") + .parse_from_str("Invalid url") .end() - .unwrap_or(0); - PriorityFeeOracleConfig::Constant(fee) - } - "helius" => { - let config = HeliusPriorityFeeOracleConfig { - url: value_parser - .chain(err) - .get_key("url") - .parse_from_str("Invalid url") - .end() - .unwrap(), - fee_level: parse_helius_priority_fee_level(&value_parser, err), - }; - PriorityFeeOracleConfig::Helius(config) - } - _ => { - err.push( - &value_parser.cwp + "type", - eyre!("Unknown priority fee oracle type"), - ); - PriorityFeeOracleConfig::Constant(0) - } + .unwrap(), + fee_level: parse_helius_priority_fee_level(&value_parser, err), + }; + Some(PriorityFeeOracleConfig::Helius(config)) } - }) - .unwrap_or_default(); + _ => { + err.push( + &value_parser.cwp + "type", + eyre!("Unknown priority fee oracle type"), + ); + None + } + } + } else { + // If not specified at all, use default + Some(PriorityFeeOracleConfig::default()) + }; priority_fee_oracle } @@ -287,6 +298,51 @@ fn parse_helius_priority_fee_level( } } +fn parse_transaction_submitter_config( + chain: &ValueParser, + err: &mut ConfigParsingError, +) -> Option { + let submitter_type = chain + .chain(err) + .get_opt_key("transactionSubmitter") + .get_key("type") + .parse_string() + .end(); + + if let Some(submitter_type) = submitter_type { + match submitter_type.to_lowercase().as_str() { + "rpc" => { + let url = chain + .chain(err) + .get_opt_key("transactionSubmitter") + .get_key("url") + .parse_from_str("Invalid url") + .end(); + Some(h_sealevel::TransactionSubmitterConfig::Rpc { url }) + } + "jito" => { + let url = chain + .chain(err) + .get_opt_key("transactionSubmitter") + .get_key("url") + .parse_from_str("Invalid url") + .end(); + Some(h_sealevel::TransactionSubmitterConfig::Jito { url }) + } + _ => { + err.push( + &chain.cwp + "transactionSubmitter.type", + eyre!("Unknown transaction submitter type"), + ); + None + } + } + } else { + // If not specified at all, use default + Some(h_sealevel::TransactionSubmitterConfig::default()) + } +} + pub fn build_connection_conf( domain_protocol: HyperlaneDomainProtocol, rpcs: &[Url], @@ -307,14 +363,10 @@ pub fn build_connection_conf( .iter() .next() .map(|url| ChainConnectionConf::Fuel(h_fuel::ConnectionConf { url: url.clone() })), - HyperlaneDomainProtocol::Sealevel => rpcs.iter().next().map(|url| { - ChainConnectionConf::Sealevel(build_sealevel_connection_conf( - url, - chain, - err, - operation_batch, - )) - }), + HyperlaneDomainProtocol::Sealevel => rpcs + .iter() + .next() + .and_then(|url| build_sealevel_connection_conf(url, chain, err, operation_batch)), HyperlaneDomainProtocol::Cosmos => { build_cosmos_connection_conf(rpcs, chain, err, operation_batch) } From 0b61be62dab83954982dea8aa5af6f936e8a1d9c Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Tue, 10 Dec 2024 22:17:12 -0700 Subject: [PATCH 21/33] probably better config parsing --- .../src/settings/parser/connection_parser.rs | 6 +- .../config/environments/mainnet3/agent.ts | 25 ++- .../mainnet3/aw-validators/rc.json | 144 +++++++++++++----- typescript/infra/src/agents/index.ts | 21 ++- typescript/infra/src/config/agent/agent.ts | 10 +- typescript/sdk/src/index.ts | 2 + typescript/sdk/src/metadata/agentConfig.ts | 15 ++ 7 files changed, 175 insertions(+), 48 deletions(-) diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index 2375464415..087e9e041a 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -305,7 +305,7 @@ fn parse_transaction_submitter_config( let submitter_type = chain .chain(err) .get_opt_key("transactionSubmitter") - .get_key("type") + .get_opt_key("type") .parse_string() .end(); @@ -315,7 +315,7 @@ fn parse_transaction_submitter_config( let url = chain .chain(err) .get_opt_key("transactionSubmitter") - .get_key("url") + .get_opt_key("url") .parse_from_str("Invalid url") .end(); Some(h_sealevel::TransactionSubmitterConfig::Rpc { url }) @@ -324,7 +324,7 @@ fn parse_transaction_submitter_config( let url = chain .chain(err) .get_opt_key("transactionSubmitter") - .get_key("url") + .get_opt_key("url") .parse_from_str("Invalid url") .end(); Some(h_sealevel::TransactionSubmitterConfig::Jito { url }) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index e232405fd2..c81559f39b 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -2,6 +2,8 @@ import { AgentSealevelHeliusFeeLevel, AgentSealevelPriorityFeeOracle, AgentSealevelPriorityFeeOracleType, + AgentSealevelTransactionSubmitter, + AgentSealevelTransactionSubmitterType, ChainName, GasPaymentEnforcement, GasPaymentEnforcementPolicyType, @@ -382,6 +384,22 @@ const sealevelPriorityFeeOracleConfigGetter = ( }; }; +const sealevelTransactionSubmitterConfigGetter = ( + chain: ChainName, +): AgentSealevelTransactionSubmitter => { + // Special case for Solana mainnet + if (chain === 'solanamainnet') { + return { + type: AgentSealevelTransactionSubmitterType.Jito, + }; + } + + // For all other chains, use the default RPC transaction submitter + return { + type: AgentSealevelTransactionSubmitterType.Rpc, + }; +}; + const contextBase = { namespace: environment, runEnv: environment, @@ -389,7 +407,10 @@ const contextBase = { aws: { region: 'us-east-1', }, - sealevelPriorityFeeOracleConfigGetter, + sealevel: { + priorityFeeOracleConfigGetter: sealevelPriorityFeeOracleConfigGetter, + transactionSubmitterConfigGetter: sealevelTransactionSubmitterConfigGetter, + }, } as const; const gasPaymentEnforcement: GasPaymentEnforcement[] = [ @@ -529,7 +550,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '172da22-20241210-031655', + tag: 'e0d8da2-20241211-044021', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json index 55638fa670..c5f4fc5bbe 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json @@ -1,6 +1,8 @@ { "ancient8": { - "validators": ["0xaae4d879a04e3d8b956eb4ffbefd57fdbed09cae"] + "validators": [ + "0xaae4d879a04e3d8b956eb4ffbefd57fdbed09cae" + ] }, "arbitrum": { "validators": [ @@ -10,10 +12,14 @@ ] }, "astar": { - "validators": ["0x50792eee9574f1bcd002e2c6dddf2aa73b04e254"] + "validators": [ + "0x50792eee9574f1bcd002e2c6dddf2aa73b04e254" + ] }, "astarzkevm": { - "validators": ["0x71e0a93f84548765cafc64d787eede19eea1ab06"] + "validators": [ + "0x71e0a93f84548765cafc64d787eede19eea1ab06" + ] }, "avalanche": { "validators": [ @@ -30,10 +36,14 @@ ] }, "bitlayer": { - "validators": ["0xec567167f07479bcd02a66c2ea66fd329cb9e22f"] + "validators": [ + "0xec567167f07479bcd02a66c2ea66fd329cb9e22f" + ] }, "blast": { - "validators": ["0x5b32f226e472da6ca19abfe1a29d5d28102a2d1a"] + "validators": [ + "0x5b32f226e472da6ca19abfe1a29d5d28102a2d1a" + ] }, "bsc": { "validators": [ @@ -50,22 +60,34 @@ ] }, "cheesechain": { - "validators": ["0xa481835355309ed46540c742a1c04b58380aa7b4"] + "validators": [ + "0xa481835355309ed46540c742a1c04b58380aa7b4" + ] }, "coredao": { - "validators": ["0x9767df5bcebce5ca89fdca5efbfb3b891cf30c5b"] + "validators": [ + "0x9767df5bcebce5ca89fdca5efbfb3b891cf30c5b" + ] }, "cyber": { - "validators": ["0x84976d5012da6b180462742f2edfae2f9d740c82"] + "validators": [ + "0x84976d5012da6b180462742f2edfae2f9d740c82" + ] }, "degenchain": { - "validators": ["0xac3734bb61e7ddda947b718b930c4067f0ccde7e"] + "validators": [ + "0xac3734bb61e7ddda947b718b930c4067f0ccde7e" + ] }, "dogechain": { - "validators": ["0x1e74735379a96586ec31f0a31aa4ee86016404a9"] + "validators": [ + "0x1e74735379a96586ec31f0a31aa4ee86016404a9" + ] }, "eclipsemainnet": { - "validators": ["0x283065cb17f98386c2b3656650d8b85e05862c8e"] + "validators": [ + "0x283065cb17f98386c2b3656650d8b85e05862c8e" + ] }, "ethereum": { "validators": [ @@ -75,13 +97,19 @@ ] }, "everclear": { - "validators": ["0x1b4455316aa859c9b3d2b13868490b7c97a9da3e"] + "validators": [ + "0x1b4455316aa859c9b3d2b13868490b7c97a9da3e" + ] }, "flare": { - "validators": ["0x35c3ce34f27def61c2d878ff7c3e35fbab08e92a"] + "validators": [ + "0x35c3ce34f27def61c2d878ff7c3e35fbab08e92a" + ] }, "fraxtal": { - "validators": ["0x8c772b730c8deb333dded14cb462e577a06283da"] + "validators": [ + "0x8c772b730c8deb333dded14cb462e577a06283da" + ] }, "gnosis": { "validators": [ @@ -98,16 +126,24 @@ ] }, "kroma": { - "validators": ["0x9b3142f04dd67585c9287f1aee42ff9754a955d3"] + "validators": [ + "0x9b3142f04dd67585c9287f1aee42ff9754a955d3" + ] }, "linea": { - "validators": ["0xad4886b6f5f5088c7ae53b69d1ff5cfc2a17bec4"] + "validators": [ + "0xad4886b6f5f5088c7ae53b69d1ff5cfc2a17bec4" + ] }, "lisk": { - "validators": ["0xa38e875985174ed2db358a2dea143e9df825880e"] + "validators": [ + "0xa38e875985174ed2db358a2dea143e9df825880e" + ] }, "lukso": { - "validators": ["0x0815a98af4285d82a65d4c006a8f100ea58f80cb"] + "validators": [ + "0x0815a98af4285d82a65d4c006a8f100ea58f80cb" + ] }, "mantapacific": { "validators": [ @@ -117,19 +153,29 @@ ] }, "merlin": { - "validators": ["0x93b08ce35b7676270b4aefda5e01c668cc61cb5c"] + "validators": [ + "0x93b08ce35b7676270b4aefda5e01c668cc61cb5c" + ] }, "metis": { - "validators": ["0xa01a62b1a6663e888399788ff15e784b5d223661"] + "validators": [ + "0xa01a62b1a6663e888399788ff15e784b5d223661" + ] }, "mint": { - "validators": ["0xbd8808980b59459cea0435d1d0c72a05e7660ff3"] + "validators": [ + "0xbd8808980b59459cea0435d1d0c72a05e7660ff3" + ] }, "mode": { - "validators": ["0x2f04ed30b1c27ef8e9e6acd360728d9bd5c3a9e2"] + "validators": [ + "0x2f04ed30b1c27ef8e9e6acd360728d9bd5c3a9e2" + ] }, "molten": { - "validators": ["0x2e433ffc514a874537ca2473af51ebba4c863409"] + "validators": [ + "0x2e433ffc514a874537ca2473af51ebba4c863409" + ] }, "moonbeam": { "validators": [ @@ -139,7 +185,9 @@ ] }, "oortmainnet": { - "validators": ["0x83f406ff315e90ae9589fa7786bf700e7c7a06f1"] + "validators": [ + "0x83f406ff315e90ae9589fa7786bf700e7c7a06f1" + ] }, "optimism": { "validators": [ @@ -163,16 +211,24 @@ ] }, "proofofplay": { - "validators": ["0xc18b802596b5d939c35c8e58afa5ef0872d3f792"] + "validators": [ + "0xc18b802596b5d939c35c8e58afa5ef0872d3f792" + ] }, "real": { - "validators": ["0x005c82fe794ef230153d22584f295d43dfc89438"] + "validators": [ + "0x005c82fe794ef230153d22584f295d43dfc89438" + ] }, "redstone": { - "validators": ["0x51ed7127c0afc0513a0f141e910c5e02b2a9a4b5"] + "validators": [ + "0x51ed7127c0afc0513a0f141e910c5e02b2a9a4b5" + ] }, "sanko": { - "validators": ["0xe12dcb5cab4a2979ac46c045fe7da3c93cf62808"] + "validators": [ + "0xe12dcb5cab4a2979ac46c045fe7da3c93cf62808" + ] }, "scroll": { "validators": [ @@ -182,16 +238,24 @@ ] }, "sei": { - "validators": ["0x846e48a7e85e5403cc690a347e1ad3c3dca11b6e"] + "validators": [ + "0x846e48a7e85e5403cc690a347e1ad3c3dca11b6e" + ] }, "shibarium": { - "validators": ["0x7f52b98a0a3e1ced83cf4eced8c0fa61886e7268"] + "validators": [ + "0x7f52b98a0a3e1ced83cf4eced8c0fa61886e7268" + ] }, "solanamainnet": { - "validators": ["0x7bd1536cb7505cf2ea1dc6744127d91fbe87a2ad"] + "validators": [ + "0x7bd1536cb7505cf2ea1dc6744127d91fbe87a2ad" + ] }, "tangle": { - "validators": ["0xd778d113975b2fe45eda424bf93c28f32a90b076"] + "validators": [ + "0xd778d113975b2fe45eda424bf93c28f32a90b076" + ] }, "viction": { "validators": [ @@ -201,16 +265,24 @@ ] }, "worldchain": { - "validators": ["0x385a2452930a0681d3ea4e40fb7722095142afcc"] + "validators": [ + "0x385a2452930a0681d3ea4e40fb7722095142afcc" + ] }, "xai": { - "validators": ["0x5c38bb0d73220755b5884978c9e7602552c30929"] + "validators": [ + "0x5c38bb0d73220755b5884978c9e7602552c30929" + ] }, "xlayer": { - "validators": ["0xa68e98cb98190485847581c8004b40ee81cbc723"] + "validators": [ + "0xa68e98cb98190485847581c8004b40ee81cbc723" + ] }, "zetachain": { - "validators": ["0xa13d146b47242671466e4041f5fe68d22a2ffe09"] + "validators": [ + "0xa13d146b47242671466e4041f5fe68d22a2ffe09" + ] }, "neutron": { "validators": [ diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index 4ba9585e9b..24fccbac07 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -2,9 +2,8 @@ import fs from 'fs'; import { join } from 'path'; import { - AgentChainMetadata, - AgentSealevelHeliusFeeLevel, - AgentSealevelPriorityFeeOracleType, + AgentSealevelPriorityFeeOracle, + AgentSealevelTransactionSubmitter, ChainName, RelayerConfig, RpcConsensusType, @@ -95,11 +94,20 @@ export abstract class AgentHelmManager extends HelmManager throw new Error(`No reorg period found for chain ${chain}`); } - let priorityFeeOracle: AgentChainMetadata['priorityFeeOracle']; - + let priorityFeeOracle: AgentSealevelPriorityFeeOracle | undefined; if (getChain(chain).protocol === ProtocolType.Sealevel) { priorityFeeOracle = - this.config.rawConfig.sealevelPriorityFeeOracleConfigGetter?.( + this.config.rawConfig.sealevel?.priorityFeeOracleConfigGetter?.( + chain, + ); + } + + let transactionSubmitter: + | AgentSealevelTransactionSubmitter + | undefined; + if (getChain(chain).protocol === ProtocolType.Sealevel) { + transactionSubmitter = + this.config.rawConfig.sealevel?.transactionSubmitterConfigGetter?.( chain, ); } @@ -111,6 +119,7 @@ export abstract class AgentHelmManager extends HelmManager blocks: { reorgPeriod }, maxBatchSize: 32, priorityFeeOracle, + transactionSubmitter, }; }), }, diff --git a/typescript/infra/src/config/agent/agent.ts b/typescript/infra/src/config/agent/agent.ts index 4a59145096..57296b8efb 100644 --- a/typescript/infra/src/config/agent/agent.ts +++ b/typescript/infra/src/config/agent/agent.ts @@ -1,6 +1,7 @@ import { AgentChainMetadata, AgentSealevelPriorityFeeOracle, + AgentSealevelTransactionSubmitter, AgentSignerAwsKey, AgentSignerKeyType, ChainName, @@ -86,9 +87,16 @@ export interface AgentContextConfig extends AgentEnvConfig { rolesWithKeys: Role[]; // Names of chains this context cares about (subset of environmentChainNames) contextChainNames: AgentChainNames; - sealevelPriorityFeeOracleConfigGetter?: ( + sealevel?: SealevelAgentConfig; +} + +export interface SealevelAgentConfig { + priorityFeeOracleConfigGetter?: ( chain: ChainName, ) => AgentSealevelPriorityFeeOracle; + transactionSubmitterConfigGetter?: ( + chain: ChainName, + ) => AgentSealevelTransactionSubmitter; } // incomplete common agent configuration for a role diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 9393e7c31c..bd1f786f9c 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -213,6 +213,8 @@ export { AgentSealevelHeliusFeeLevel, AgentSealevelPriorityFeeOracle, AgentSealevelPriorityFeeOracleType, + AgentSealevelTransactionSubmitter, + AgentSealevelTransactionSubmitterType, AgentSigner, AgentSignerAwsKey, AgentSignerHexKey, diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index 1b7d277cdb..480ce037f8 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -65,6 +65,11 @@ export enum AgentSealevelHeliusFeeLevel { UnsafeMax = 'unsafeMax', } +export enum AgentSealevelTransactionSubmitterType { + Rpc = 'rpc', + Jito = 'jito', +} + const AgentSignerHexKeySchema = z .object({ type: z.literal(AgentSignerKeyType.Hex).optional(), @@ -150,14 +155,24 @@ const AgentSealevelChainMetadataSchema = z.object({ }), ]) .optional(), + transactionSubmitter: z + .object({ + type: z.nativeEnum(AgentSealevelTransactionSubmitterType), + url: z.string().optional(), + }) + .optional(), }); export type AgentSealevelChainMetadata = z.infer< typeof AgentSealevelChainMetadataSchema >; + export type AgentSealevelPriorityFeeOracle = AgentSealevelChainMetadata['priorityFeeOracle']; +export type AgentSealevelTransactionSubmitter = + AgentSealevelChainMetadata['transactionSubmitter']; + export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( HyperlaneDeploymentArtifactsSchema, ) From 74afde87ab65c15aac9d68bcced930391f041887 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Tue, 10 Dec 2024 22:56:32 -0700 Subject: [PATCH 22/33] update agent config --- rust/main/config/mainnet_config.json | 7 ++++--- typescript/infra/config/environments/mainnet3/agent.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index dc66d22345..037faaa73a 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -785,7 +785,7 @@ "index": { "from": 1, "mode": "sequence", - "chunk": 100 + "chunk": 20 }, "interchainGasPaymaster": "ABb3i11z7wKoGCfeRQNQbVYWjAm7jG7HzZnDLV4RKRbK", "mailbox": "EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y", @@ -2747,7 +2747,8 @@ "gasCurrencyCoinGeckoId": "sei-network", "gnosisSafeTransactionServiceUrl": "https://transaction.sei-safe.protofire.io", "index": { - "from": 80809403 + "from": 80809403, + "chunk": 1000 }, "interchainAccountIsm": "0xf35dc7B9eE4Ebf0cd3546Bd6EE3b403dE2b9F5D6", "interchainAccountRouter": "0xBcaedE97a98573A88242B3b0CB0A255F3f90d4d5", @@ -2813,7 +2814,7 @@ "index": { "from": 1, "mode": "sequence", - "chunk": 100 + "chunk": 20 }, "interchainGasPaymaster": "JAvHW21tYXE9dtdG83DReqU2b4LUexFuCbtJT5tF8X6M", "mailbox": "E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi", diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index c81559f39b..b598a99d46 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -550,7 +550,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'e0d8da2-20241211-044021', + tag: '0b61be6-20241211-051755', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. From 236569215d843f15e236d6fcc43d5649dc2f3bb8 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Wed, 11 Dec 2024 21:51:37 -0700 Subject: [PATCH 23/33] Move to recommended, reshuffle some stuff --- .../chains/hyperlane-sealevel/src/mailbox.rs | 152 +++--------------- .../hyperlane-sealevel/src/priority_fee.rs | 122 ++++++++------ .../hyperlane-sealevel/src/rpc/client.rs | 50 ++++++ .../hyperlane-sealevel/src/trait_builder.rs | 6 +- 4 files changed, 156 insertions(+), 174 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index 1c84a80a23..1b6281abcf 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -40,7 +40,7 @@ use solana_sdk::{ instruction::{AccountMeta, Instruction}, message::Message, pubkey::Pubkey, - signature::Signature, + signature::{self, Signature}, signer::{keypair::Keypair, Signer as _}, transaction::{Transaction, VersionedTransaction}, }; @@ -324,16 +324,28 @@ impl SealevelMailbox { .rpc() .simulate_transaction(&simulation_tx) .await?; - tracing::warn!(?simulation_result, "Got simulation result for transaction"); + tracing::debug!(?simulation_result, "Got simulation result for transaction"); + + // If there was an error in the simulation result, return an error. + if simulation_result.err.is_some() { + return Err(ChainCommunicationError::from_other_str( + format!("Error in simulation result: {:?}", simulation_result.err).as_str(), + )); + } + + // Get the compute units used in the simulation result, requiring + // that it is greater than 0. let simulation_compute_units: u32 = simulation_result .units_consumed + .and_then(|units| if units > 0 { Some(units) } else { None }) .ok_or_else(|| { ChainCommunicationError::from_other_str( - "No compute units used in simulation result", + "Empty or zero compute units returned in simulation result", ) })? .try_into() .map_err(ChainCommunicationError::from_other)?; + // Bump the compute units by 10% to ensure we have enough, but cap it at the max. let simulation_compute_units = MAX_COMPUTE_UNITS.min((simulation_compute_units * 11) / 10); @@ -342,7 +354,7 @@ impl SealevelMailbox { .get_priority_fee(&simulation_tx) .await?; - tracing::warn!( + tracing::info!( ?priority_fee, ?simulation_compute_units, "Got priority fee and compute units for transaction" @@ -489,129 +501,6 @@ impl SealevelMailbox { Ok(process_instruction) } - // async fn send_and_confirm_transaction( - // &self, - // transaction: &Transaction, - // ) -> ChainResult { - // if self.is_solana() { - // self.send_and_confirm_transaction_with_jito(transaction) - // .await - // } else { - // self.provider - // .rpc() - // .send_transaction(transaction, true) - // .await - // } - - // // if self.is_solana() { - // // if let PriorityFeeOracleConfig::Helius(helius) = &self.priority_fee_oracle_config { - // // let rpc = SealevelRpcClient::new(helius.url.clone().into()); - // // return rpc.send_transaction(transaction, true).await; - // // } else { - // // tracing::warn!("Priority fee oracle is not Helius, falling back to normal RPC"); - // // } - // // } - // // self.provider - // // .rpc() - // // .send_transaction(transaction, true) - // // .await - // } - - // // Stolen from Solana's non-blocking client, but with Jito! - // pub async fn send_and_confirm_transaction_with_jito( - // &self, - // transaction: &impl SerializableTransaction, - // ) -> ChainResult { - // let signature = transaction.get_signature(); - - // let base58_txn = bs58::encode( - // bincode::serialize(&transaction).map_err(ChainCommunicationError::from_other)?, - // ) - // .into_string(); - - // const SEND_RETRIES: usize = 1; - // const GET_STATUS_RETRIES: usize = usize::MAX; - - // 'sending: for _ in 0..SEND_RETRIES { - // let jito_request_body = serde_json::json!({ - // "jsonrpc": "2.0", - // "id": 1, - // "method": "sendBundle", - // "params": [ - // [base58_txn] - // ], - // }); - - // tracing::info!( - // ?jito_request_body, - // ?signature, - // "Sending sealevel transaction to Jito as bundle" - // ); - - // let jito_response = reqwest::Client::new() - // .post("https://mainnet.block-engine.jito.wtf:443/api/v1/bundles") - // .json(&jito_request_body) - // .send() - // .await - // .map_err(ChainCommunicationError::from_other)?; - // let jito_response_text = jito_response.text().await; - - // tracing::info!( - // ?signature, - // ?jito_response_text, - // "Got Jito response for sealevel transaction bundle" - // ); - - // // let recent_blockhash = if transaction.uses_durable_nonce() { - // // self.provider - // // .rpc() - // // .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) - // // .await? - // // } else { - // // *transaction.get_recent_blockhash() - // // }; - - // // for status_retry in 0..GET_STATUS_RETRIES { - // // let signature_statuses: Response>> = self - // // .provider - // // .rpc() - // // .get_signature_statuses(&[*signature]) - // // .await?; - // // let signature_status = signature_statuses.value.first().cloned().flatten(); - // // match signature_status { - // // Some(_) => return Ok(*signature), - // // None => { - // // if !self - // // .provider - // // .rpc() - // // .is_blockhash_valid(&recent_blockhash) - // // .await? - // // { - // // // Block hash is not found by some reason - // // break 'sending; - // // } else if cfg!(not(test)) - // // // Ignore sleep at last step. - // // && status_retry < GET_STATUS_RETRIES - // // { - // // // Retry twice a second - // // tokio::time::sleep(std::time::Duration::from_millis(500)).await; - // // continue; - // // } - // // } - // // } - // // } - // } - - // Err(ChainCommunicationError::from_other( - // solana_client::rpc_request::RpcError::ForUser( - // "unable to confirm transaction. \ - // This can happen in situations such as transaction expiration \ - // and insufficient fee-payer funds" - // .to_string(), - // ), - // )) - // } - async fn get_inbox(&self) -> ChainResult> { let account = self .rpc() @@ -727,6 +616,15 @@ impl Mailbox for SealevelMailbox { tracing::info!(?tx, ?signature, "Sealevel transaction sent"); + let send_instant = std::time::Instant::now(); + + // Wait for the transaction to be confirmed. + self.rpc().wait_for_transaction_confirmation(&tx).await?; + + tracing::info!(?tx, ?signature, time_to_confirm=?send_instant.elapsed(), "Sealevel transaction confirmed"); + + // TODO: not sure if this actually checks if the transaction was executed / reverted? + // Confirm the transaction. let executed = self .rpc() .confirm_transaction_with_commitment(&signature, commitment) diff --git a/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs index a58c6530b4..d3602e8552 100644 --- a/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs +++ b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs @@ -44,6 +44,20 @@ impl HeliusPriorityFeeOracle { config, } } + + fn get_priority_fee_estimate_options(&self) -> serde_json::Value { + if let HeliusPriorityFeeLevel::Recommended = self.config.fee_level { + serde_json::json!({ + "recommended": true, + "transactionEncoding": "base58", + }) + } else { + serde_json::json!({ + "priorityLevel": self.config.fee_level, + "transactionEncoding": "base58", + }) + } + } } #[async_trait] @@ -61,10 +75,7 @@ impl PriorityFeeOracle for HeliusPriorityFeeOracle { "params": [ { "transaction": base58_tx, - "options": { - "includeAllPriorityFeeLevels": true, - "transactionEncoding": "base58", - } + "options": self.get_priority_fee_estimate_options(), } ], }); @@ -82,14 +93,9 @@ impl PriorityFeeOracle for HeliusPriorityFeeOracle { .await .map_err(ChainCommunicationError::from_other)?; - tracing::warn!(priority_fee_levels = ?response.result.priority_fee_levels, "Fetched priority fee levels"); + tracing::debug!(?response, "Fetched priority fee from Helius API"); - let fee = response - .result - .priority_fee_levels - .get_priority_fee(&self.config.fee_level) - .ok_or_else(|| ChainCommunicationError::from_other_str("Priority fee level not found"))? - .round() as u64; + let fee = response.result.priority_fee_estimate.round() as u64; Ok(fee) } @@ -106,38 +112,17 @@ struct JsonRpcResult { #[derive(Debug, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] struct GetPriorityFeeEstimateResult { - priority_fee_levels: PriorityFeeLevelsResponse, -} - -#[derive(Debug, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -struct PriorityFeeLevelsResponse { - min: Option, - low: Option, - medium: Option, - high: Option, - very_high: Option, - unsafe_max: Option, -} - -impl PriorityFeeLevelsResponse { - fn get_priority_fee(&self, level: &HeliusPriorityFeeLevel) -> Option { - match level { - HeliusPriorityFeeLevel::Min => self.min, - HeliusPriorityFeeLevel::Low => self.low, - HeliusPriorityFeeLevel::Medium => self.medium, - HeliusPriorityFeeLevel::High => self.high, - HeliusPriorityFeeLevel::VeryHigh => self.very_high, - HeliusPriorityFeeLevel::UnsafeMax => self.unsafe_max, - } - } + priority_fee_estimate: f64, } #[cfg(test)] mod test { use solana_sdk::{bs58, transaction::Transaction}; - use crate::priority_fee::{PriorityFeeLevelsResponse, PriorityFeeOracle}; + use crate::{ + priority_fee::{HeliusPriorityFeeOracle, PriorityFeeOracle}, + HeliusPriorityFeeLevel, HeliusPriorityFeeOracleConfig, + }; use super::{GetPriorityFeeEstimateResult, JsonRpcResult}; @@ -164,21 +149,66 @@ mod test { oracle.get_priority_fee(&transaction).await.unwrap(); } + #[test] + fn test_helius_get_priority_fee_estimate_options_ser() { + let get_oracle = |fee_level| { + HeliusPriorityFeeOracle::new(HeliusPriorityFeeOracleConfig { + url: url::Url::parse("http://localhost:8080").unwrap(), + fee_level, + }) + }; + + // When the fee level is Recommended, ensure `recommended` is set to true + let oracle = get_oracle(HeliusPriorityFeeLevel::Recommended); + + let options = oracle.get_priority_fee_estimate_options(); + let expected = serde_json::json!({ + "recommended": true, + "transactionEncoding": "base58", + }); + assert_eq!(options, expected); + + // When the fee level is not Recommended, ensure `priorityLevel` is set + let oracle = get_oracle(HeliusPriorityFeeLevel::Medium); + + let options = oracle.get_priority_fee_estimate_options(); + let expected = serde_json::json!({ + "priorityLevel": "Medium", + "transactionEncoding": "base58", + }); + assert_eq!(options, expected); + + // Ensure the serialization of HeliusPriorityFeeLevel is PascalCase, + // as required by the API https://docs.helius.dev/solana-apis/priority-fee-api#helius-priority-fee-api + let serialized = serde_json::json!([ + HeliusPriorityFeeLevel::Recommended, + HeliusPriorityFeeLevel::Min, + HeliusPriorityFeeLevel::Low, + HeliusPriorityFeeLevel::Medium, + HeliusPriorityFeeLevel::High, + HeliusPriorityFeeLevel::VeryHigh, + HeliusPriorityFeeLevel::UnsafeMax, + ]); + let expected = serde_json::json!([ + "Recommended", + "Min", + "Low", + "Medium", + "High", + "VeryHigh", + "UnsafeMax" + ]); + assert_eq!(serialized, expected); + } + #[test] fn test_helius_get_priority_fee_estimate_deser() { - let text = r#"{"jsonrpc":"2.0","result":{"priorityFeeLevels":{"min":0.0,"low":0.0,"medium":1000.0,"high":225000.0,"veryHigh":9000000.0,"unsafeMax":2340000000.0}},"id":"1"}"#; + let text = r#"{"jsonrpc":"2.0","result":{"priorityFeeEstimate":1000.0},"id":"1"}"#; let response: JsonRpcResult = serde_json::from_str(text).unwrap(); let expected = GetPriorityFeeEstimateResult { - priority_fee_levels: PriorityFeeLevelsResponse { - min: Some(0.0), - low: Some(0.0), - medium: Some(1000.0), - high: Some(225000.0), - very_high: Some(9000000.0), - unsafe_max: Some(2340000000.0), - }, + priority_fee_estimate: 1000.0, }; assert_eq!(response.result, expected); } diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 557a37a9e5..fa6532719c 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -3,6 +3,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use serializable_account_meta::{SerializableAccountMeta, SimulationReturnData}; use solana_client::{ nonblocking::rpc_client::RpcClient, + rpc_client::SerializableTransaction, rpc_config::{ RpcBlockConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig, RpcTransactionConfig, @@ -220,6 +221,55 @@ impl SealevelRpcClient { .map_err(ChainCommunicationError::from_other) } + // Standalone logic stolen from Solana's non-blocking client, + // decoupled from the sending of a transaction. + pub async fn wait_for_transaction_confirmation( + &self, + transaction: &impl SerializableTransaction, + ) -> ChainResult<()> { + let signature = transaction.get_signature(); + + const GET_STATUS_RETRIES: usize = usize::MAX; + + let recent_blockhash = if transaction.uses_durable_nonce() { + self.get_latest_blockhash_with_commitment(CommitmentConfig::processed()) + .await? + } else { + *transaction.get_recent_blockhash() + }; + + for status_retry in 0..GET_STATUS_RETRIES { + let signature_statuses: Response>> = + self.get_signature_statuses(&[*signature]).await?; + let signature_status = signature_statuses.value.first().cloned().flatten(); + match signature_status { + Some(_) => return Ok(()), + None => { + if !self.is_blockhash_valid(&recent_blockhash).await? { + // Block hash is not found by some reason + break; + } else if cfg!(not(test)) + // Ignore sleep at last step. + && status_retry < GET_STATUS_RETRIES + { + // Retry twice a second + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + continue; + } + } + } + } + + Err(ChainCommunicationError::from_other( + solana_client::rpc_request::RpcError::ForUser( + "unable to confirm transaction. \ + This can happen in situations such as transaction expiration \ + and insufficient fee-payer funds" + .to_string(), + ), + )) + } + /// Simulates an instruction, and attempts to deserialize it into a T. /// If no return data at all was returned, returns Ok(None). /// If some return data was returned but deserialization was unsuccessful, diff --git a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs index ea3ec914d5..50747ef8e3 100644 --- a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs +++ b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs @@ -1,4 +1,5 @@ use hyperlane_core::{config::OperationBatchConfig, ChainCommunicationError, NativeToken}; +use serde::Serialize; use url::Url; use crate::{ @@ -71,8 +72,11 @@ pub struct HeliusPriorityFeeOracleConfig { } /// The priority fee level to use -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub enum HeliusPriorityFeeLevel { + /// 50th percentile, but a floor of 10k microlamports. + /// The floor results in a staked Helius connection being used. (https://docs.helius.dev/guides/sending-transactions-on-solana#staked-connections) + Recommended, /// 0th percentile Min, /// 10th percentile From 4124a15920ae4e236d11292447a8b032efa64237 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Wed, 11 Dec 2024 21:56:10 -0700 Subject: [PATCH 24/33] some cleanup --- .../hyperlane-sealevel/src/trait_builder.rs | 3 +- .../src/settings/parser/connection_parser.rs | 45 +++++++++++-------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs index 50747ef8e3..13cc0b90a4 100644 --- a/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs +++ b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs @@ -72,10 +72,11 @@ pub struct HeliusPriorityFeeOracleConfig { } /// The priority fee level to use -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, Default)] pub enum HeliusPriorityFeeLevel { /// 50th percentile, but a floor of 10k microlamports. /// The floor results in a staked Helius connection being used. (https://docs.helius.dev/guides/sending-transactions-on-solana#staked-connections) + #[default] Recommended, /// 0th percentile Min, diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index 087e9e041a..ff9ec9d409 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -243,6 +243,10 @@ fn parse_priority_fee_oracle_config( Some(PriorityFeeOracleConfig::Constant(fee)) } "helius" => { + let fee_level = parse_helius_priority_fee_level(&value_parser, err); + if !err.is_ok() { + return None; + } let config = HeliusPriorityFeeOracleConfig { url: value_parser .chain(err) @@ -250,7 +254,7 @@ fn parse_priority_fee_oracle_config( .parse_from_str("Invalid url") .end() .unwrap(), - fee_level: parse_helius_priority_fee_level(&value_parser, err), + fee_level: fee_level.unwrap(), }; Some(PriorityFeeOracleConfig::Helius(config)) } @@ -273,28 +277,33 @@ fn parse_priority_fee_oracle_config( fn parse_helius_priority_fee_level( value_parser: &ValueParser, err: &mut ConfigParsingError, -) -> HeliusPriorityFeeLevel { +) -> Option { let level = value_parser .chain(err) - .get_key("feeLevel") + .get_opt_key("feeLevel") .parse_string() - .end() - .unwrap_or_default(); + .end(); - match level.to_lowercase().as_str() { - "min" => HeliusPriorityFeeLevel::Min, - "low" => HeliusPriorityFeeLevel::Low, - "medium" => HeliusPriorityFeeLevel::Medium, - "high" => HeliusPriorityFeeLevel::High, - "veryhigh" => HeliusPriorityFeeLevel::VeryHigh, - "unsafemax" => HeliusPriorityFeeLevel::UnsafeMax, - _ => { - err.push( - &value_parser.cwp + "feeLevel", - eyre!("Unknown priority fee level"), - ); - HeliusPriorityFeeLevel::Medium + if let Some(level) = level { + match level.to_lowercase().as_str() { + "recommended" => Some(HeliusPriorityFeeLevel::Recommended), + "min" => Some(HeliusPriorityFeeLevel::Min), + "low" => Some(HeliusPriorityFeeLevel::Low), + "medium" => Some(HeliusPriorityFeeLevel::Medium), + "high" => Some(HeliusPriorityFeeLevel::High), + "veryhigh" => Some(HeliusPriorityFeeLevel::VeryHigh), + "unsafemax" => Some(HeliusPriorityFeeLevel::UnsafeMax), + _ => { + err.push( + &value_parser.cwp + "feeLevel", + eyre!("Unknown priority fee level"), + ); + None + } } + } else { + // If not specified at all, use the default + Some(HeliusPriorityFeeLevel::default()) } } From 1fe1914056663944bc34053b0120a7c195fe011b Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 12 Dec 2024 21:11:19 -0700 Subject: [PATCH 25/33] Estimate message delivery success on SVM --- .../chains/hyperlane-sealevel/src/mailbox.rs | 60 +++++++++++++++---- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index 2553a51b56..ff8135980c 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -100,6 +100,12 @@ lazy_static! { (pubkey!("CuQmsT4eSF4dYiiGUGYYQxJ7c58pUAD5ADE3BbFGzQKx"), pubkey!("EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm")), ]); } + +struct SealevelTxCostEstimate { + compute_units: u32, + compute_unit_price_micro_lamports: u64, +} + /// A reference to a Mailbox contract on some Sealevel chain pub struct SealevelMailbox { pub(crate) program_id: Pubkey, @@ -311,11 +317,10 @@ impl SealevelMailbox { self.get_account_metas(instruction).await } - /// Builds a transaction with estimated costs for a given instruction. - async fn build_estimated_tx_for_instruction( + async fn get_estimated_costs_for_instruction( &self, instruction: Instruction, - ) -> ChainResult { + ) -> ChainResult { // Build a transaction that sets the max compute units and a dummy compute unit price. // This is used for simulation to get the actual compute unit limit. We set dummy values // for the compute unit limit and price because we want to include the instructions that @@ -359,17 +364,36 @@ impl SealevelMailbox { .get_priority_fee(&simulation_tx) .await?; + Ok(SealevelTxCostEstimate { + compute_units: simulation_compute_units, + compute_unit_price_micro_lamports: priority_fee, + }) + } + + /// Builds a transaction with estimated costs for a given instruction. + async fn build_estimated_tx_for_instruction( + &self, + instruction: Instruction, + ) -> ChainResult { + // Get the estimated costs for the instruction. + let SealevelTxCostEstimate { + compute_units, + compute_unit_price_micro_lamports, + } = self + .get_estimated_costs_for_instruction(instruction.clone()) + .await?; + tracing::info!( - ?priority_fee, - ?simulation_compute_units, - "Got priority fee and compute units for transaction" + ?compute_units, + ?compute_unit_price_micro_lamports, + "Got compute units and compute unit price / priority fee for transaction" ); // Build the final transaction with the correct compute unit limit and price. let tx = self .create_transaction_for_instruction( - simulation_compute_units, - priority_fee, + compute_units, + compute_unit_price_micro_lamports, instruction, true, ) @@ -647,10 +671,24 @@ impl Mailbox for SealevelMailbox { #[instrument(err, ret, skip(self))] async fn process_estimate_costs( &self, - _message: &HyperlaneMessage, - _metadata: &[u8], + message: &HyperlaneMessage, + metadata: &[u8], ) -> ChainResult { - // TODO use correct data upon integrating IGP support + // Getting a process instruction in Sealevel is a pretty expensive operation + // that involves some view calls. Consider reusing the instruction with subsequent + // calls to `process` to avoid this cost. + let process_instruction = self.get_process_instruction(message, metadata).await?; + + // The retuend costs are unused at the moment - we simply want to perform a simulation to + // determine if the message will revert or not. + let _ = self + .get_estimated_costs_for_instruction(process_instruction) + .await?; + + // TODO use correct data upon integrating IGP support. + // NOTE: providing a real gas limit here will result in accurately enforcing + // gas payments. Be careful rolling this out to not impact existing contracts + // that may not be paying for super accurate gas amounts. Ok(TxCostEstimate { gas_limit: U256::zero(), gas_price: FixedPointNumber::zero(), From 26e1ed86864b46c288d464b03598cd749024acbd Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 12 Dec 2024 21:12:55 -0700 Subject: [PATCH 26/33] prettier --- .../mainnet3/aw-validators/rc.json | 144 +++++------------- 1 file changed, 36 insertions(+), 108 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json index c5f4fc5bbe..55638fa670 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json @@ -1,8 +1,6 @@ { "ancient8": { - "validators": [ - "0xaae4d879a04e3d8b956eb4ffbefd57fdbed09cae" - ] + "validators": ["0xaae4d879a04e3d8b956eb4ffbefd57fdbed09cae"] }, "arbitrum": { "validators": [ @@ -12,14 +10,10 @@ ] }, "astar": { - "validators": [ - "0x50792eee9574f1bcd002e2c6dddf2aa73b04e254" - ] + "validators": ["0x50792eee9574f1bcd002e2c6dddf2aa73b04e254"] }, "astarzkevm": { - "validators": [ - "0x71e0a93f84548765cafc64d787eede19eea1ab06" - ] + "validators": ["0x71e0a93f84548765cafc64d787eede19eea1ab06"] }, "avalanche": { "validators": [ @@ -36,14 +30,10 @@ ] }, "bitlayer": { - "validators": [ - "0xec567167f07479bcd02a66c2ea66fd329cb9e22f" - ] + "validators": ["0xec567167f07479bcd02a66c2ea66fd329cb9e22f"] }, "blast": { - "validators": [ - "0x5b32f226e472da6ca19abfe1a29d5d28102a2d1a" - ] + "validators": ["0x5b32f226e472da6ca19abfe1a29d5d28102a2d1a"] }, "bsc": { "validators": [ @@ -60,34 +50,22 @@ ] }, "cheesechain": { - "validators": [ - "0xa481835355309ed46540c742a1c04b58380aa7b4" - ] + "validators": ["0xa481835355309ed46540c742a1c04b58380aa7b4"] }, "coredao": { - "validators": [ - "0x9767df5bcebce5ca89fdca5efbfb3b891cf30c5b" - ] + "validators": ["0x9767df5bcebce5ca89fdca5efbfb3b891cf30c5b"] }, "cyber": { - "validators": [ - "0x84976d5012da6b180462742f2edfae2f9d740c82" - ] + "validators": ["0x84976d5012da6b180462742f2edfae2f9d740c82"] }, "degenchain": { - "validators": [ - "0xac3734bb61e7ddda947b718b930c4067f0ccde7e" - ] + "validators": ["0xac3734bb61e7ddda947b718b930c4067f0ccde7e"] }, "dogechain": { - "validators": [ - "0x1e74735379a96586ec31f0a31aa4ee86016404a9" - ] + "validators": ["0x1e74735379a96586ec31f0a31aa4ee86016404a9"] }, "eclipsemainnet": { - "validators": [ - "0x283065cb17f98386c2b3656650d8b85e05862c8e" - ] + "validators": ["0x283065cb17f98386c2b3656650d8b85e05862c8e"] }, "ethereum": { "validators": [ @@ -97,19 +75,13 @@ ] }, "everclear": { - "validators": [ - "0x1b4455316aa859c9b3d2b13868490b7c97a9da3e" - ] + "validators": ["0x1b4455316aa859c9b3d2b13868490b7c97a9da3e"] }, "flare": { - "validators": [ - "0x35c3ce34f27def61c2d878ff7c3e35fbab08e92a" - ] + "validators": ["0x35c3ce34f27def61c2d878ff7c3e35fbab08e92a"] }, "fraxtal": { - "validators": [ - "0x8c772b730c8deb333dded14cb462e577a06283da" - ] + "validators": ["0x8c772b730c8deb333dded14cb462e577a06283da"] }, "gnosis": { "validators": [ @@ -126,24 +98,16 @@ ] }, "kroma": { - "validators": [ - "0x9b3142f04dd67585c9287f1aee42ff9754a955d3" - ] + "validators": ["0x9b3142f04dd67585c9287f1aee42ff9754a955d3"] }, "linea": { - "validators": [ - "0xad4886b6f5f5088c7ae53b69d1ff5cfc2a17bec4" - ] + "validators": ["0xad4886b6f5f5088c7ae53b69d1ff5cfc2a17bec4"] }, "lisk": { - "validators": [ - "0xa38e875985174ed2db358a2dea143e9df825880e" - ] + "validators": ["0xa38e875985174ed2db358a2dea143e9df825880e"] }, "lukso": { - "validators": [ - "0x0815a98af4285d82a65d4c006a8f100ea58f80cb" - ] + "validators": ["0x0815a98af4285d82a65d4c006a8f100ea58f80cb"] }, "mantapacific": { "validators": [ @@ -153,29 +117,19 @@ ] }, "merlin": { - "validators": [ - "0x93b08ce35b7676270b4aefda5e01c668cc61cb5c" - ] + "validators": ["0x93b08ce35b7676270b4aefda5e01c668cc61cb5c"] }, "metis": { - "validators": [ - "0xa01a62b1a6663e888399788ff15e784b5d223661" - ] + "validators": ["0xa01a62b1a6663e888399788ff15e784b5d223661"] }, "mint": { - "validators": [ - "0xbd8808980b59459cea0435d1d0c72a05e7660ff3" - ] + "validators": ["0xbd8808980b59459cea0435d1d0c72a05e7660ff3"] }, "mode": { - "validators": [ - "0x2f04ed30b1c27ef8e9e6acd360728d9bd5c3a9e2" - ] + "validators": ["0x2f04ed30b1c27ef8e9e6acd360728d9bd5c3a9e2"] }, "molten": { - "validators": [ - "0x2e433ffc514a874537ca2473af51ebba4c863409" - ] + "validators": ["0x2e433ffc514a874537ca2473af51ebba4c863409"] }, "moonbeam": { "validators": [ @@ -185,9 +139,7 @@ ] }, "oortmainnet": { - "validators": [ - "0x83f406ff315e90ae9589fa7786bf700e7c7a06f1" - ] + "validators": ["0x83f406ff315e90ae9589fa7786bf700e7c7a06f1"] }, "optimism": { "validators": [ @@ -211,24 +163,16 @@ ] }, "proofofplay": { - "validators": [ - "0xc18b802596b5d939c35c8e58afa5ef0872d3f792" - ] + "validators": ["0xc18b802596b5d939c35c8e58afa5ef0872d3f792"] }, "real": { - "validators": [ - "0x005c82fe794ef230153d22584f295d43dfc89438" - ] + "validators": ["0x005c82fe794ef230153d22584f295d43dfc89438"] }, "redstone": { - "validators": [ - "0x51ed7127c0afc0513a0f141e910c5e02b2a9a4b5" - ] + "validators": ["0x51ed7127c0afc0513a0f141e910c5e02b2a9a4b5"] }, "sanko": { - "validators": [ - "0xe12dcb5cab4a2979ac46c045fe7da3c93cf62808" - ] + "validators": ["0xe12dcb5cab4a2979ac46c045fe7da3c93cf62808"] }, "scroll": { "validators": [ @@ -238,24 +182,16 @@ ] }, "sei": { - "validators": [ - "0x846e48a7e85e5403cc690a347e1ad3c3dca11b6e" - ] + "validators": ["0x846e48a7e85e5403cc690a347e1ad3c3dca11b6e"] }, "shibarium": { - "validators": [ - "0x7f52b98a0a3e1ced83cf4eced8c0fa61886e7268" - ] + "validators": ["0x7f52b98a0a3e1ced83cf4eced8c0fa61886e7268"] }, "solanamainnet": { - "validators": [ - "0x7bd1536cb7505cf2ea1dc6744127d91fbe87a2ad" - ] + "validators": ["0x7bd1536cb7505cf2ea1dc6744127d91fbe87a2ad"] }, "tangle": { - "validators": [ - "0xd778d113975b2fe45eda424bf93c28f32a90b076" - ] + "validators": ["0xd778d113975b2fe45eda424bf93c28f32a90b076"] }, "viction": { "validators": [ @@ -265,24 +201,16 @@ ] }, "worldchain": { - "validators": [ - "0x385a2452930a0681d3ea4e40fb7722095142afcc" - ] + "validators": ["0x385a2452930a0681d3ea4e40fb7722095142afcc"] }, "xai": { - "validators": [ - "0x5c38bb0d73220755b5884978c9e7602552c30929" - ] + "validators": ["0x5c38bb0d73220755b5884978c9e7602552c30929"] }, "xlayer": { - "validators": [ - "0xa68e98cb98190485847581c8004b40ee81cbc723" - ] + "validators": ["0xa68e98cb98190485847581c8004b40ee81cbc723"] }, "zetachain": { - "validators": [ - "0xa13d146b47242671466e4041f5fe68d22a2ffe09" - ] + "validators": ["0xa13d146b47242671466e4041f5fe68d22a2ffe09"] }, "neutron": { "validators": [ From 669c85f5d6cc067354c05c9d25fa45a38a980cca Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 12 Dec 2024 21:22:07 -0700 Subject: [PATCH 27/33] documenting --- .../main/chains/hyperlane-sealevel/src/priority_fee.rs | 10 ++++++++++ .../main/chains/hyperlane-sealevel/src/tx_submitter.rs | 8 +++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs index d3602e8552..a82085fce8 100644 --- a/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs +++ b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs @@ -8,11 +8,14 @@ use solana_sdk::{bs58, transaction::Transaction}; use crate::{HeliusPriorityFeeLevel, HeliusPriorityFeeOracleConfig}; +/// A trait for fetching the priority fee for a transaction. #[async_trait] pub trait PriorityFeeOracle: Send + Sync { + /// Fetch the priority fee in microlamports for a transaction. async fn get_priority_fee(&self, transaction: &Transaction) -> ChainResult; } +/// A priority fee oracle that returns a constant fee. #[derive(Debug, Clone)] pub struct ConstantPriorityFeeOracle { fee: u64, @@ -31,6 +34,8 @@ impl PriorityFeeOracle for ConstantPriorityFeeOracle { } } +/// A priority fee oracle that fetches the fee from the Helius API. +/// https://docs.helius.dev/solana-apis/priority-fee-api #[derive(Debug, Clone)] pub struct HeliusPriorityFeeOracle { client: Client, @@ -46,6 +51,9 @@ impl HeliusPriorityFeeOracle { } fn get_priority_fee_estimate_options(&self) -> serde_json::Value { + // It's an odd interface, but if using the Recommended fee level, the API requires `recommended: true`, + // otherwise it requires `priorityLevel: ""`. + if let HeliusPriorityFeeLevel::Recommended = self.config.fee_level { serde_json::json!({ "recommended": true, @@ -101,6 +109,7 @@ impl PriorityFeeOracle for HeliusPriorityFeeOracle { } } +/// The result of a JSON-RPC request to the Helius API. #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct JsonRpcResult { @@ -109,6 +118,7 @@ struct JsonRpcResult { result: T, } +/// The result of a `getPriorityFeeEstimate` request to the Helius API. #[derive(Debug, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] struct GetPriorityFeeEstimateResult { diff --git a/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs b/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs index 6ac5643bb6..0935ac3947 100644 --- a/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs +++ b/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs @@ -9,8 +9,10 @@ use solana_sdk::{ use crate::SealevelRpcClient; +/// A trait for submitting transactions to the chain. #[async_trait] pub trait TransactionSubmitter: Send + Sync { + /// Get the instruction to set the compute unit price. fn get_priority_fee_instruction( &self, compute_unit_price_micro_lamports: u64, @@ -18,6 +20,7 @@ pub trait TransactionSubmitter: Send + Sync { payer: &Pubkey, ) -> Instruction; + /// Send a transaction to the chain. async fn send_transaction( &self, transaction: &Transaction, @@ -25,6 +28,7 @@ pub trait TransactionSubmitter: Send + Sync { ) -> ChainResult; } +/// A transaction submitter that uses the vanilla RPC to submit transactions. #[derive(Debug)] pub struct RpcTransactionSubmitter { rpc_client: SealevelRpcClient, @@ -64,13 +68,15 @@ impl TransactionSubmitter for RpcTransactionSubmitter { } } +/// A transaction submitter that uses the Jito API to submit transactions. #[derive(Debug)] pub struct JitoTransactionSubmitter { rpc_client: SealevelRpcClient, } impl JitoTransactionSubmitter { - // From https://docs.jito.wtf/lowlatencytxnsend/#sendtransaction + /// The minimum tip to include in a transaction. + /// From https://docs.jito.wtf/lowlatencytxnsend/#sendtransaction const MINIMUM_TIP_LAMPORTS: u64 = 1000; pub fn new(url: String) -> Self { From b98678cfd7a985fccfe2422b04950d70f60f2e3e Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 12 Dec 2024 21:36:45 -0700 Subject: [PATCH 28/33] move some fns into the RPC client --- .../chains/hyperlane-sealevel/src/mailbox.rs | 156 ++---------------- .../hyperlane-sealevel/src/rpc/client.rs | 156 +++++++++++++++++- 2 files changed, 170 insertions(+), 142 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index ff8135980c..82c77390a0 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -27,12 +27,9 @@ use solana_sdk::{ account::Account, clock::Slot, commitment_config::CommitmentConfig, - compute_budget::ComputeBudgetInstruction, instruction::{AccountMeta, Instruction}, - message::Message, pubkey::Pubkey, signer::{keypair::Keypair, Signer as _}, - transaction::Transaction, }; use tracing::{debug, info, instrument, warn}; @@ -60,9 +57,6 @@ const SPL_NOOP: &str = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"; // TODO: consider a more sane value and/or use IGP gas payments instead. const PROCESS_COMPUTE_UNITS: u32 = 1_400_000; -/// The max amount of compute units for a transaction. -const MAX_COMPUTE_UNITS: u32 = 1_400_000; - /// 0.0005 SOL, in lamports. /// A typical tx fee without a prioritization fee is 0.000005 SOL, or /// 5000 lamports. (Example: https://explorer.solana.com/tx/fNd3xVeBzFHeuzr8dXQxLGiHMzTeYpykSV25xWzNRaHtzzjvY9A3MzXh1ZsK2JncRHkwtuWrGEwGXVhFaUCYhtx) @@ -101,11 +95,6 @@ lazy_static! { ]); } -struct SealevelTxCostEstimate { - compute_units: u32, - compute_unit_price_micro_lamports: u64, -} - /// A reference to a Mailbox contract on some Sealevel chain pub struct SealevelMailbox { pub(crate) program_id: Pubkey, @@ -317,134 +306,6 @@ impl SealevelMailbox { self.get_account_metas(instruction).await } - async fn get_estimated_costs_for_instruction( - &self, - instruction: Instruction, - ) -> ChainResult { - // Build a transaction that sets the max compute units and a dummy compute unit price. - // This is used for simulation to get the actual compute unit limit. We set dummy values - // for the compute unit limit and price because we want to include the instructions that - // set these in the cost estimate. - let simulation_tx = self - .create_transaction_for_instruction(MAX_COMPUTE_UNITS, 0, instruction.clone(), false) - .await?; - - let simulation_result = self - .provider - .rpc() - .simulate_transaction(&simulation_tx) - .await?; - tracing::debug!(?simulation_result, "Got simulation result for transaction"); - - // If there was an error in the simulation result, return an error. - if simulation_result.err.is_some() { - return Err(ChainCommunicationError::from_other_str( - format!("Error in simulation result: {:?}", simulation_result.err).as_str(), - )); - } - - // Get the compute units used in the simulation result, requiring - // that it is greater than 0. - let simulation_compute_units: u32 = simulation_result - .units_consumed - .and_then(|units| if units > 0 { Some(units) } else { None }) - .ok_or_else(|| { - ChainCommunicationError::from_other_str( - "Empty or zero compute units returned in simulation result", - ) - })? - .try_into() - .map_err(ChainCommunicationError::from_other)?; - - // Bump the compute units by 10% to ensure we have enough, but cap it at the max. - let simulation_compute_units = MAX_COMPUTE_UNITS.min((simulation_compute_units * 11) / 10); - - let priority_fee = self - .priority_fee_oracle - .get_priority_fee(&simulation_tx) - .await?; - - Ok(SealevelTxCostEstimate { - compute_units: simulation_compute_units, - compute_unit_price_micro_lamports: priority_fee, - }) - } - - /// Builds a transaction with estimated costs for a given instruction. - async fn build_estimated_tx_for_instruction( - &self, - instruction: Instruction, - ) -> ChainResult { - // Get the estimated costs for the instruction. - let SealevelTxCostEstimate { - compute_units, - compute_unit_price_micro_lamports, - } = self - .get_estimated_costs_for_instruction(instruction.clone()) - .await?; - - tracing::info!( - ?compute_units, - ?compute_unit_price_micro_lamports, - "Got compute units and compute unit price / priority fee for transaction" - ); - - // Build the final transaction with the correct compute unit limit and price. - let tx = self - .create_transaction_for_instruction( - compute_units, - compute_unit_price_micro_lamports, - instruction, - true, - ) - .await?; - - Ok(tx) - } - - /// Creates a transaction for a given instruction, compute unit limit, and compute unit price. - /// If `blockhash` is `None`, the latest blockhash is fetched from the RPC. - async fn create_transaction_for_instruction( - &self, - compute_unit_limit: u32, - compute_unit_price_micro_lamports: u64, - instruction: Instruction, - sign: bool, - ) -> ChainResult { - let payer = self.get_payer()?; - - let instructions = vec![ - // Set the compute unit limit. - ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit), - // Set the priority fee / tip - self.tx_submitter.get_priority_fee_instruction( - compute_unit_price_micro_lamports, - compute_unit_limit.into(), - &payer.pubkey(), - ), - instruction, - ]; - - let tx = if sign { - let recent_blockhash = self - .rpc() - .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) - .await - .map_err(ChainCommunicationError::from_other)?; - - Transaction::new_signed_with_payer( - &instructions, - Some(&payer.pubkey()), - &[payer], - recent_blockhash, - ) - } else { - Transaction::new_unsigned(Message::new(&instructions, Some(&payer.pubkey()))) - }; - - Ok(tx) - } - async fn get_process_instruction( &self, message: &HyperlaneMessage, @@ -633,7 +494,14 @@ impl Mailbox for SealevelMailbox { let process_instruction = self.get_process_instruction(message, metadata).await?; let tx = self - .build_estimated_tx_for_instruction(process_instruction) + .provider + .rpc() + .build_estimated_tx_for_instruction( + process_instruction, + self.get_payer()?, + &*self.tx_submitter, + &*self.priority_fee_oracle, + ) .await?; tracing::info!(?tx, "Created sealevel transaction to process message"); @@ -682,7 +550,13 @@ impl Mailbox for SealevelMailbox { // The retuend costs are unused at the moment - we simply want to perform a simulation to // determine if the message will revert or not. let _ = self - .get_estimated_costs_for_instruction(process_instruction) + .rpc() + .get_estimated_costs_for_instruction( + process_instruction, + self.get_payer()?, + &*self.tx_submitter, + &*self.priority_fee_oracle, + ) .await?; // TODO use correct data upon integrating IGP support. diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index fa6532719c..63ad2e7368 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -13,6 +13,7 @@ use solana_client::{ use solana_sdk::{ account::Account, commitment_config::CommitmentConfig, + compute_budget::ComputeBudgetInstruction, hash::Hash, instruction::{AccountMeta, Instruction}, message::Message, @@ -27,11 +28,22 @@ use solana_transaction_status::{ use hyperlane_core::{ChainCommunicationError, ChainResult, U256}; -use crate::error::HyperlaneSealevelError; +use crate::{ + error::HyperlaneSealevelError, priority_fee::PriorityFeeOracle, + tx_submitter::TransactionSubmitter, +}; + +pub struct SealevelTxCostEstimate { + compute_units: u32, + compute_unit_price_micro_lamports: u64, +} pub struct SealevelRpcClient(RpcClient); impl SealevelRpcClient { + /// The max amount of compute units for a transaction. + const MAX_COMPUTE_UNITS: u32 = 1_400_000; + pub fn new(rpc_endpoint: String) -> Self { Self(RpcClient::new_with_commitment( rpc_endpoint, @@ -327,6 +339,148 @@ impl SealevelRpcClient { Ok(result) } + /// Gets the estimated costs for a given instruction. + pub async fn get_estimated_costs_for_instruction( + &self, + instruction: Instruction, + payer: &Keypair, + tx_submitter: &dyn TransactionSubmitter, + priority_fee_oracle: &dyn PriorityFeeOracle, + ) -> ChainResult { + // Build a transaction that sets the max compute units and a dummy compute unit price. + // This is used for simulation to get the actual compute unit limit. We set dummy values + // for the compute unit limit and price because we want to include the instructions that + // set these in the cost estimate. + let simulation_tx = self + .create_transaction_for_instruction( + Self::MAX_COMPUTE_UNITS, + 0, + instruction.clone(), + payer, + tx_submitter, + false, + ) + .await?; + + let simulation_result = self.simulate_transaction(&simulation_tx).await?; + tracing::debug!(?simulation_result, "Got simulation result for transaction"); + + // If there was an error in the simulation result, return an error. + if simulation_result.err.is_some() { + return Err(ChainCommunicationError::from_other_str( + format!("Error in simulation result: {:?}", simulation_result.err).as_str(), + )); + } + + // Get the compute units used in the simulation result, requiring + // that it is greater than 0. + let simulation_compute_units: u32 = simulation_result + .units_consumed + .and_then(|units| if units > 0 { Some(units) } else { None }) + .ok_or_else(|| { + ChainCommunicationError::from_other_str( + "Empty or zero compute units returned in simulation result", + ) + })? + .try_into() + .map_err(ChainCommunicationError::from_other)?; + + // Bump the compute units by 10% to ensure we have enough, but cap it at the max. + let simulation_compute_units = + Self::MAX_COMPUTE_UNITS.min((simulation_compute_units * 11) / 10); + + let priority_fee = priority_fee_oracle.get_priority_fee(&simulation_tx).await?; + + Ok(SealevelTxCostEstimate { + compute_units: simulation_compute_units, + compute_unit_price_micro_lamports: priority_fee, + }) + } + + /// Builds a transaction with estimated costs for a given instruction. + pub async fn build_estimated_tx_for_instruction( + &self, + instruction: Instruction, + payer: &Keypair, + tx_submitter: &dyn TransactionSubmitter, + priority_fee_oracle: &dyn PriorityFeeOracle, + ) -> ChainResult { + // Get the estimated costs for the instruction. + let SealevelTxCostEstimate { + compute_units, + compute_unit_price_micro_lamports, + } = self + .get_estimated_costs_for_instruction( + instruction.clone(), + payer, + tx_submitter, + priority_fee_oracle, + ) + .await?; + + tracing::info!( + ?compute_units, + ?compute_unit_price_micro_lamports, + "Got compute units and compute unit price / priority fee for transaction" + ); + + // Build the final transaction with the correct compute unit limit and price. + let tx = self + .create_transaction_for_instruction( + compute_units, + compute_unit_price_micro_lamports, + instruction, + payer, + tx_submitter, + true, + ) + .await?; + + Ok(tx) + } + + /// Creates a transaction for a given instruction, compute unit limit, and compute unit price. + /// If `blockhash` is `None`, the latest blockhash is fetched from the RPC. + pub async fn create_transaction_for_instruction( + &self, + compute_unit_limit: u32, + compute_unit_price_micro_lamports: u64, + instruction: Instruction, + payer: &Keypair, + tx_submitter: &dyn TransactionSubmitter, + sign: bool, + ) -> ChainResult { + let instructions = vec![ + // Set the compute unit limit. + ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit), + // Set the priority fee / tip + tx_submitter.get_priority_fee_instruction( + compute_unit_price_micro_lamports, + compute_unit_limit.into(), + &payer.pubkey(), + ), + instruction, + ]; + + let tx = if sign { + let recent_blockhash = self + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) + .await + .map_err(ChainCommunicationError::from_other)?; + + Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer], + recent_blockhash, + ) + } else { + Transaction::new_unsigned(Message::new(&instructions, Some(&payer.pubkey()))) + }; + + Ok(tx) + } + pub fn url(&self) -> String { self.0.url() } From 356c1072c3f2ae45593383fa6e30e90c47f13829 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 12 Dec 2024 22:11:42 -0700 Subject: [PATCH 29/33] final touches --- .../chains/hyperlane-sealevel/src/mailbox.rs | 22 ------------------- .../hyperlane-sealevel/src/rpc/client.rs | 13 +++++++---- .../templates/external-secret.yaml | 9 ++++++-- .../templates/relayer-statefulset.yaml | 2 ++ .../templates/scraper-statefulset.yaml | 2 ++ .../templates/validator-statefulset.yaml | 2 ++ .../config/environments/mainnet3/agent.ts | 6 +++-- typescript/infra/src/config/agent/agent.ts | 3 +++ 8 files changed, 29 insertions(+), 30 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index 82c77390a0..eebf2aafa8 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -53,28 +53,6 @@ use crate::{ConnectionConf, SealevelProvider, SealevelRpcClient}; const SYSTEM_PROGRAM: &str = "11111111111111111111111111111111"; const SPL_NOOP: &str = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"; -// The max amount of compute units for a transaction. -// TODO: consider a more sane value and/or use IGP gas payments instead. -const PROCESS_COMPUTE_UNITS: u32 = 1_400_000; - -/// 0.0005 SOL, in lamports. -/// A typical tx fee without a prioritization fee is 0.000005 SOL, or -/// 5000 lamports. (Example: https://explorer.solana.com/tx/fNd3xVeBzFHeuzr8dXQxLGiHMzTeYpykSV25xWzNRaHtzzjvY9A3MzXh1ZsK2JncRHkwtuWrGEwGXVhFaUCYhtx) -/// See average priority fees here https://solanacompass.com/statistics/fees -/// to inform what to spend here. -const PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX: u64 = 500000; - -/// In micro-lamports. Multiply this by the compute units to figure out -/// the additional cost of processing a message, in addition to the mandatory -/// "base" cost of signature verification. -/// Unused at the moment, but kept for future reference. -#[allow(dead_code)] -const PROCESS_COMPUTE_UNIT_PRICE_MICRO_LAMPORTS: u64 = - // Convert to micro-lamports - (PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX * 1_000_000) - // Divide by the max compute units - / PROCESS_COMPUTE_UNITS as u64; - // Earlier versions of collateral warp routes were deployed off a version where the mint // was requested as a writeable account for handle instruction. This is not necessary, // and generally requires a higher priority fee to be paid. diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 63ad2e7368..0e6612ecd1 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -233,8 +233,10 @@ impl SealevelRpcClient { .map_err(ChainCommunicationError::from_other) } - // Standalone logic stolen from Solana's non-blocking client, - // decoupled from the sending of a transaction. + /// Polls the RPC until the transaction is confirmed or the blockhash + /// expires. + /// Standalone logic stolen from Solana's non-blocking client, + /// decoupled from the sending of a transaction. pub async fn wait_for_transaction_confirmation( &self, transaction: &impl SerializableTransaction, @@ -440,7 +442,7 @@ impl SealevelRpcClient { } /// Creates a transaction for a given instruction, compute unit limit, and compute unit price. - /// If `blockhash` is `None`, the latest blockhash is fetched from the RPC. + /// If `sign` is true, the transaction will be signed. pub async fn create_transaction_for_instruction( &self, compute_unit_limit: u32, @@ -463,8 +465,11 @@ impl SealevelRpcClient { ]; let tx = if sign { + // Getting the finalized blockhash eliminates the chance the blockhash + // gets reorged out, causing the tx to be invalid. The tradeoff is this + // will cause the tx to expire in about 47 seconds (instead of the typical 60). let recent_blockhash = self - .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) + .get_latest_blockhash_with_commitment(CommitmentConfig::finalized()) .await .map_err(ChainCommunicationError::from_other)?; diff --git a/rust/main/helm/hyperlane-agent/templates/external-secret.yaml b/rust/main/helm/hyperlane-agent/templates/external-secret.yaml index 7d169c40f8..f3a6980d21 100644 --- a/rust/main/helm/hyperlane-agent/templates/external-secret.yaml +++ b/rust/main/helm/hyperlane-agent/templates/external-secret.yaml @@ -30,9 +30,14 @@ spec: {{- if eq .protocol "cosmos" }} HYP_CHAINS_{{ .name | upper }}_CUSTOMGRPCURLS: {{ printf "'{{ .%s_grpcs | mustFromJson | join \",\" }}'" .name }} {{- end }} - {{- if and (eq .protocol "sealevel") (eq ((.priorityFeeOracle).type) "helius") }} + {{- if eq .protocol "sealevel" }} + {{- if eq ((.priorityFeeOracle).type) "helius" }} HYP_CHAINS_{{ .name | upper }}_PRIORITYFEEORACLE_URL: {{ printf "'{{ .%s_helius }}'" .name }} {{- end }} + {{- if eq ((.transactionSubmitter).url) "helius" }} + HYP_CHAINS_{{ .name | upper }}_TRANSACTIONSUBMITTER_URL: {{ printf "'{{ .%s_helius }}'" .name }} + {{- end }} + {{- end }} {{- end }} data: {{- /* @@ -48,7 +53,7 @@ spec: remoteRef: key: {{ printf "%s-grpc-endpoints-%s" $.Values.hyperlane.runEnv .name }} {{- end }} - {{- if and (eq .protocol "sealevel") (eq ((.priorityFeeOracle).type) "helius") }} + {{- if and (eq .protocol "sealevel") (or (eq ((.priorityFeeOracle).type) "helius") (eq ((.transactionSubmitter).url) "helius")) }} - secretKey: {{ printf "%s_helius" .name }} remoteRef: key: {{ printf "%s-rpc-endpoint-helius-%s" $.Values.hyperlane.runEnv .name }} diff --git a/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml index da69543d8e..47be9ad2e9 100644 --- a/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml +++ b/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml @@ -17,7 +17,9 @@ spec: metadata: annotations: checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/external-secret: {{ include (print $.Template.BasePath "/external-secret.yaml") . | sha256sum }} checksum/relayer-configmap: {{ include (print $.Template.BasePath "/relayer-configmap.yaml") . | sha256sum }} + checksum/relayer-external-secret: {{ include (print $.Template.BasePath "/relayer-external-secret.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml index 06326e260c..1b419e1123 100644 --- a/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml +++ b/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml @@ -17,6 +17,8 @@ spec: metadata: annotations: checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/external-secret: {{ include (print $.Template.BasePath "/external-secret.yaml") . | sha256sum }} + checksum/scraper-external-secret: {{ include (print $.Template.BasePath "/scraper-external-secret.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml index 1b0a87dd41..b5929bfd99 100644 --- a/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml +++ b/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml @@ -17,7 +17,9 @@ spec: metadata: annotations: checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/external-secret: {{ include (print $.Template.BasePath "/external-secret.yaml") . | sha256sum }} checksum/validator-configmap: {{ include (print $.Template.BasePath "/validator-configmap.yaml") . | sha256sum }} + checksum/scraper-external-secret: {{ include (print $.Template.BasePath "/scraper-external-secret.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 59d8386c49..cf3e227d0f 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -12,6 +12,7 @@ import { import { AgentChainConfig, + HELIUS_SECRET_URL_MARKER, RootAgentConfig, getAgentChainNamesFromConfig, } from '../../../src/config/agent/agent.js'; @@ -388,7 +389,8 @@ const sealevelTransactionSubmitterConfigGetter = ( // Special case for Solana mainnet if (chain === 'solanamainnet') { return { - type: AgentSealevelTransactionSubmitterType.Jito, + type: AgentSealevelTransactionSubmitterType.Rpc, + url: HELIUS_SECRET_URL_MARKER, }; } @@ -548,7 +550,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '0b61be6-20241211-051755', + tag: 'b98678c-20241213-043725', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. diff --git a/typescript/infra/src/config/agent/agent.ts b/typescript/infra/src/config/agent/agent.ts index 57296b8efb..7d2dbe54b6 100644 --- a/typescript/infra/src/config/agent/agent.ts +++ b/typescript/infra/src/config/agent/agent.ts @@ -99,6 +99,9 @@ export interface SealevelAgentConfig { ) => AgentSealevelTransactionSubmitter; } +// An ugly way to mark a URL as a the secret Helius URL when Helm templating +export const HELIUS_SECRET_URL_MARKER = 'helius'; + // incomplete common agent configuration for a role interface AgentRoleConfig { // K8s-specific From 570c52b58be500acbc04ec5011a831e804a984bf Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 13 Dec 2024 14:42:35 -0700 Subject: [PATCH 30/33] add recommended --- typescript/infra/config/environments/mainnet3/agent.ts | 5 ++--- typescript/sdk/src/metadata/agentConfig.ts | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index cf3e227d0f..b2bf182b1d 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -370,8 +370,8 @@ const sealevelPriorityFeeOracleConfigGetter = ( if (chain === 'solanamainnet') { return { type: AgentSealevelPriorityFeeOracleType.Helius, - feeLevel: AgentSealevelHeliusFeeLevel.Medium, - // URL is populated by the external secrets in the helm chart + feeLevel: AgentSealevelHeliusFeeLevel.Recommended, + // URL is auto populated by the external secrets in the helm chart url: '', }; } @@ -520,7 +520,6 @@ const hyperlane: RootAgentConfig = { gasPaymentEnforcement: gasPaymentEnforcement, metricAppContextsGetter, resources: relayerResources, - // blacklist: [...warpRouteMatchingList('WIF/eclipsemainnet-solanamainnet')], }, validators: { docker: { diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index 480ce037f8..9ff999c0dc 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -57,6 +57,7 @@ export enum AgentSealevelPriorityFeeOracleType { } export enum AgentSealevelHeliusFeeLevel { + Recommended = 'recommended', Min = 'min', Low = 'low', Medium = 'medium', From ad9d1274ab9f594821c4691ed03a600aa131fe11 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 23 Dec 2024 13:02:53 -0700 Subject: [PATCH 31/33] docs(changeset): Added new Sealevel tx submission and priority fee oracle params to agent config types --- .changeset/spotty-guests-dance.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spotty-guests-dance.md diff --git a/.changeset/spotty-guests-dance.md b/.changeset/spotty-guests-dance.md new file mode 100644 index 0000000000..39eb9b0f49 --- /dev/null +++ b/.changeset/spotty-guests-dance.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Added new Sealevel tx submission and priority fee oracle params to agent config types From effae4feb690c5c3a703f6d499f2b3ccf0107cbf Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:12:36 +0000 Subject: [PATCH 32/33] chore: log simulation result at `error` level if it failed (#5069) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/main/chains/hyperlane-sealevel/src/rpc/client.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 0e6612ecd1..d84bbe4607 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -365,13 +365,15 @@ impl SealevelRpcClient { .await?; let simulation_result = self.simulate_transaction(&simulation_tx).await?; - tracing::debug!(?simulation_result, "Got simulation result for transaction"); // If there was an error in the simulation result, return an error. if simulation_result.err.is_some() { + tracing::error!(?simulation_result, "Got simulation result for transaction"); return Err(ChainCommunicationError::from_other_str( format!("Error in simulation result: {:?}", simulation_result.err).as_str(), )); + } else { + tracing::debug!(?simulation_result, "Got simulation result for transaction"); } // Get the compute units used in the simulation result, requiring From b69dee8434ef1a2794764835fa2a590651969611 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 23 Dec 2024 13:17:18 -0700 Subject: [PATCH 33/33] PR comments --- .../chains/hyperlane-sealevel/src/mailbox.rs | 4 ++- .../hyperlane-sealevel/src/priority_fee.rs | 33 ++++++++----------- .../hyperlane-sealevel/src/rpc/client.rs | 12 +++---- .../hyperlane-sealevel/src/tx_submitter.rs | 6 ---- .../src/settings/parser/connection_parser.rs | 4 +-- 5 files changed, 24 insertions(+), 35 deletions(-) diff --git a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs index eebf2aafa8..034f24c027 100644 --- a/rust/main/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -493,6 +493,8 @@ impl Mailbox for SealevelMailbox { // Wait for the transaction to be confirmed. self.rpc().wait_for_transaction_confirmation(&tx).await?; + // We expect time_to_confirm to fluctuate depending on the commitment level when submitting the + // tx, but still use it as a proxy for tx latency to help debug. tracing::info!(?tx, ?signature, time_to_confirm=?send_instant.elapsed(), "Sealevel transaction confirmed"); // TODO: not sure if this actually checks if the transaction was executed / reverted? @@ -525,7 +527,7 @@ impl Mailbox for SealevelMailbox { // calls to `process` to avoid this cost. let process_instruction = self.get_process_instruction(message, metadata).await?; - // The retuend costs are unused at the moment - we simply want to perform a simulation to + // The returned costs are unused at the moment - we simply want to perform a simulation to // determine if the message will revert or not. let _ = self .rpc() diff --git a/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs index a82085fce8..2df395cce6 100644 --- a/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs +++ b/rust/main/chains/hyperlane-sealevel/src/priority_fee.rs @@ -1,6 +1,5 @@ -#![allow(dead_code)] - use async_trait::async_trait; +use derive_new::new; use hyperlane_core::{ChainCommunicationError, ChainResult}; use reqwest::Client; use serde::Deserialize; @@ -16,17 +15,11 @@ pub trait PriorityFeeOracle: Send + Sync { } /// A priority fee oracle that returns a constant fee. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, new)] pub struct ConstantPriorityFeeOracle { fee: u64, } -impl ConstantPriorityFeeOracle { - pub fn new(fee: u64) -> Self { - Self { fee } - } -} - #[async_trait] impl PriorityFeeOracle for ConstantPriorityFeeOracle { async fn get_priority_fee(&self, _transaction: &Transaction) -> ChainResult { @@ -54,17 +47,15 @@ impl HeliusPriorityFeeOracle { // It's an odd interface, but if using the Recommended fee level, the API requires `recommended: true`, // otherwise it requires `priorityLevel: ""`. - if let HeliusPriorityFeeLevel::Recommended = self.config.fee_level { - serde_json::json!({ - "recommended": true, - "transactionEncoding": "base58", - }) - } else { - serde_json::json!({ - "priorityLevel": self.config.fee_level, - "transactionEncoding": "base58", - }) - } + let (key, value) = match &self.config.fee_level { + HeliusPriorityFeeLevel::Recommended => ("recommended", serde_json::json!(true)), + level => ("priorityLevel", serde_json::json!(level)), + }; + + serde_json::json!({ + key: value, + "transactionEncoding": "base58", + }) } } @@ -113,7 +104,9 @@ impl PriorityFeeOracle for HeliusPriorityFeeOracle { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct JsonRpcResult { + #[allow(dead_code)] jsonrpc: String, + #[allow(dead_code)] id: String, result: T, } diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs index 0e6612ecd1..bc446bf86f 100644 --- a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -378,14 +378,14 @@ impl SealevelRpcClient { // that it is greater than 0. let simulation_compute_units: u32 = simulation_result .units_consumed - .and_then(|units| if units > 0 { Some(units) } else { None }) - .ok_or_else(|| { - ChainCommunicationError::from_other_str( - "Empty or zero compute units returned in simulation result", - ) - })? + .unwrap_or_default() .try_into() .map_err(ChainCommunicationError::from_other)?; + if simulation_compute_units == 0 { + return Err(ChainCommunicationError::from_other_str( + "Empty or zero compute units returned in simulation result", + )); + } // Bump the compute units by 10% to ensure we have enough, but cap it at the max. let simulation_compute_units = diff --git a/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs b/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs index 0935ac3947..c7468d0f24 100644 --- a/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs +++ b/rust/main/chains/hyperlane-sealevel/src/tx_submitter.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - use async_trait::async_trait; use hyperlane_core::ChainResult; use solana_sdk::{ @@ -40,10 +38,6 @@ impl RpcTransactionSubmitter { rpc_client: SealevelRpcClient::new(url), } } - - pub fn new_with_rpc_client(rpc_client: SealevelRpcClient) -> Self { - Self { rpc_client } - } } #[async_trait] diff --git a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index ff9ec9d409..b3f91ee88e 100644 --- a/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -169,7 +169,7 @@ fn build_sealevel_connection_conf( let mut local_err = ConfigParsingError::default(); let native_token = parse_native_token(chain, err, 9); - let priority_fee_oracle = parse_priority_fee_oracle_config(chain, &mut local_err); + let priority_fee_oracle = parse_sealevel_priority_fee_oracle_config(chain, &mut local_err); let transaction_submitter = parse_transaction_submitter_config(chain, &mut local_err); if !local_err.is_ok() { @@ -211,7 +211,7 @@ fn parse_native_token( } } -fn parse_priority_fee_oracle_config( +fn parse_sealevel_priority_fee_oracle_config( chain: &ValueParser, err: &mut ConfigParsingError, ) -> Option {