Skip to content

Commit

Permalink
CCCP-196, feature: optional initial gas price escalation (#77)
Browse files Browse the repository at this point in the history
* CCCP-196, feature: add an option for initial gas price escalation

* CCCP-196, fix: add from field on transaction build

* CCCP-196, fix: add missing from field setting

* CCCP-196, fix: use gas price coefficient on retry

* CCCP-196, fix: reorder legacy middleware builder

* CCCP-196, fix: rollback middleware arc
  • Loading branch information
dnjscksdn98 authored Aug 4, 2023
1 parent 505b843 commit de4ebfb
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 15 deletions.
3 changes: 0 additions & 3 deletions client/src/eth/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ pub const DEFAULT_TX_RETRY_INTERVAL_MS: u64 = 3000;
/// The coefficient that will be multiplied to the retry interval on every new retry.
pub const RETRY_TX_COEFFICIENT: u64 = 2;

/// The coefficient that will be multiplied on the previously send transaction gas price.
pub const RETRY_GAS_PRICE_COEFFICIENT: f64 = 1.2;

/// The coefficient that will be multiplied on the max fee.
pub const MAX_FEE_COEFFICIENT: u64 = 2;

Expand Down
3 changes: 3 additions & 0 deletions client/src/eth/tx/eip1559_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ impl<T: 'static + JsonRpcClient> TransactionManager<T> for Eip1559TransactionMan
sleep(Duration::from_millis(generate_delay())).await;
}

// set transaction `from` field
msg.tx_request = msg.tx_request.from(self.client.address());

// estimate the gas amount to be used
let estimated_gas =
match self.middleware.estimate_gas(&msg.tx_request.to_typed(), None).await {
Expand Down
45 changes: 37 additions & 8 deletions client/src/eth/tx/legacy_manager.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::eth::{
generate_delay, EthClient, EventMessage, TransactionManager, TxRequest, DEFAULT_CALL_RETRIES,
DEFAULT_CALL_RETRY_INTERVAL_MS, DEFAULT_TX_RETRIES, RETRY_GAS_PRICE_COEFFICIENT,
DEFAULT_CALL_RETRY_INTERVAL_MS, DEFAULT_TX_RETRIES,
};
use async_trait::async_trait;
use br_primitives::{eth::ETHEREUM_BLOCK_TIME, sub_display_format};
Expand All @@ -25,7 +25,7 @@ use tokio::{
const SUB_LOG_TARGET: &str = "legacy-tx-manager";

type LegacyMiddleware<T> = NonceManagerMiddleware<
Arc<GasEscalatorMiddleware<SignerMiddleware<Arc<Provider<T>>, LocalWallet>>>,
SignerMiddleware<Arc<GasEscalatorMiddleware<Arc<Provider<T>>>>, LocalWallet>,
>;

/// The essential task that sends legacy transactions asynchronously.
Expand All @@ -38,6 +38,12 @@ pub struct LegacyTransactionManager<T> {
receiver: UnboundedReceiver<EventMessage>,
/// The flag whether the client has enabled txpool namespace.
is_txpool_enabled: bool,
/// The flag whether if the gas price will be initially escalated. The `escalate_percentage`
/// will be used on escalation. This will only have effect on legacy transactions. (default:
/// false)
is_initially_escalated: bool,
/// The coefficient used on transaction gas price escalation (default: 1.15)
gas_price_coefficient: f64,
/// The flag whether debug mode is enabled. If enabled, certain errors will be logged such as
/// gas estimation failures.
debug_mode: bool,
Expand All @@ -53,24 +59,25 @@ impl<T: 'static + JsonRpcClient> LegacyTransactionManager<T> {
debug_mode: bool,
escalate_interval: Option<u64>,
escalate_percentage: Option<f64>,
is_initially_escalated: bool,
duplicate_confirm_delay: Option<u64>,
) -> (Self, UnboundedSender<EventMessage>) {
let (sender, receiver) = mpsc::unbounded_channel::<EventMessage>();

let mut coefficient = 1.15;
let mut gas_price_coefficient = 1.15;
if let Some(escalate_percentage) = escalate_percentage {
coefficient = 1.0 + (escalate_percentage / 100.0);
gas_price_coefficient = 1.0 + (escalate_percentage / 100.0);
}

let escalator = GeometricGasPrice::new(
coefficient,
gas_price_coefficient,
escalate_interval.unwrap_or(ETHEREUM_BLOCK_TIME),
None::<u64>,
);
let middleware = client
.get_provider()
.wrap_into(|p| SignerMiddleware::new(p, client.wallet.signer.clone()))
.wrap_into(|p| Arc::new(GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)))
.wrap_into(|p| SignerMiddleware::new(p, client.wallet.signer.clone()))
.wrap_into(|p| NonceManagerMiddleware::new(p, client.address()));

(
Expand All @@ -79,6 +86,8 @@ impl<T: 'static + JsonRpcClient> LegacyTransactionManager<T> {
middleware,
receiver,
is_txpool_enabled: false,
is_initially_escalated,
gas_price_coefficient,
debug_mode,
duplicate_confirm_delay,
},
Expand All @@ -100,11 +109,27 @@ impl<T: 'static + JsonRpcClient> LegacyTransactionManager<T> {
self.handle_failed_get_gas_price(DEFAULT_CALL_RETRIES, error.to_string()).await,
};
let escalated_gas_price =
U256::from((previous_gas_price * RETRY_GAS_PRICE_COEFFICIENT).ceil() as u64);
U256::from((previous_gas_price * self.gas_price_coefficient).ceil() as u64);

max(current_network_gas_price, escalated_gas_price)
}

/// Get gas_price for escalated legacy transaction request. This will be only used when
/// `is_initially_escalated` is enabled.
async fn get_gas_price_for_escalation(&self) -> U256 {
let current_network_gas_price = match self.middleware.get_gas_price().await {
Ok(gas_price) => {
br_metrics::increase_rpc_calls(&self.client.get_chain_name());
gas_price
},
Err(error) =>
self.handle_failed_get_gas_price(DEFAULT_CALL_RETRIES, error.to_string()).await,
};
U256::from(
(current_network_gas_price.as_u64() as f64 * self.gas_price_coefficient).ceil() as u64
)
}

/// Handles the failed gas price rpc request.
async fn handle_failed_get_gas_price(&self, retries_remaining: u8, error: String) -> U256 {
let mut retries = retries_remaining;
Expand Down Expand Up @@ -231,7 +256,11 @@ impl<T: 'static + JsonRpcClient> TransactionManager<T> for LegacyTransactionMana

// check the txpool for transaction duplication prevention
if !(self.is_duplicate_relay(&msg.tx_request, msg.check_mempool).await) {
let result = self.middleware.send_transaction(msg.tx_request.to_legacy(), None).await;
let mut tx = msg.tx_request.to_legacy();
if self.is_initially_escalated {
tx = tx.gas_price(self.get_gas_price_for_escalation().await);
}
let result = self.middleware.send_transaction(tx, None).await;
br_metrics::increase_rpc_calls(&self.client.get_chain_name());

match result {
Expand Down
1 change: 1 addition & 0 deletions configs/config.mainnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ evm_providers:
call_interval: 3000
block_confirmations: 5
is_relay_target: false
is_initially_escalated: true
socket_address: "0xb5Fa48E8B9b89760a9f9176388D1B64A8D4968dF"
vault_address: "0x78ae4c0FD4f02CA79A2d8738d3369A4Bc5D4E323"
authority_address: "0xF0500d77d5446665314722b963ab1F71872063E9"
Expand Down
1 change: 1 addition & 0 deletions configs/config.testnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ evm_providers:
call_interval: 3000
block_confirmations: 5
is_relay_target: false
is_initially_escalated: true
socket_address: "0x8039c3AD8ED55509fD3f6Daa78867923fDe6E61c"
vault_address: "0x27C66cb5caa07C9B332939c357c789C606f5054C"
authority_address: "0xCf9f6428A309b6652a1dfaA4d8aB8B61C9c7E8CF"
Expand Down
13 changes: 9 additions & 4 deletions primitives/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,21 @@ pub struct EVMProvider {
pub is_native: Option<bool>,
/// The flag whether it will handle relay transactions to the current chain.
pub is_relay_target: bool,
/// If true, enables Eip1559
/// If true, enables Eip1559. (default: false)
pub eip1559: Option<bool>,
/// The minimum priority fee required.
/// The minimum priority fee required. (default: 0)
pub min_priority_fee: Option<u64>,
/// Gas price escalate interval(seconds) when tx stuck in mempool. (default: 12)
pub escalate_interval: Option<u64>,
/// Gas price increase percentage on retry when transaction stuck in mempool. (default: 15.0)
/// Gas price increase percentage on gas price escalation such as when handling tx
/// replacements. (default: 15.0)
pub escalate_percentage: Option<f64>,
/// The flag whether if the gas price will be initially escalated. The `escalate_percentage`
/// will be used on escalation. This will only have effect on legacy transactions. (default:
/// false)
pub is_initially_escalated: Option<bool>,
/// If first relay transaction is stuck in mempool after waiting for this amount of time(ms),
/// ignore duplicate prevent logic. (default: {call_interval * 10})
/// ignore duplicate prevent logic. (default: 12s)
pub duplicate_confirm_delay: Option<u64>,
/// Socket contract address
pub socket_address: String,
Expand Down
1 change: 1 addition & 0 deletions relayer/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub fn new_relay_base(config: Configuration) -> Result<RelayBase, ServiceError>
system.debug_mode.unwrap_or(false),
evm_provider.escalate_interval,
evm_provider.escalate_percentage,
evm_provider.is_initially_escalated.unwrap_or(false),
evm_provider.duplicate_confirm_delay,
);
tx_managers.0.push(tx_manager);
Expand Down

0 comments on commit de4ebfb

Please sign in to comment.