Skip to content

Commit 32abba5

Browse files
authored
(chore): Fortuna refactor (#2372)
* extract middleware to new file * whoops * move to new module * missed files * fix build * fix imports * move file
1 parent ec30791 commit 32abba5

17 files changed

+1543
-1444
lines changed

apps/fortuna/src/chain.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,2 @@
1-
pub(crate) mod eth_gas_oracle;
21
pub(crate) mod ethereum;
3-
mod nonce_manager;
42
pub(crate) mod reader;
5-
pub(crate) mod traced_client;

apps/fortuna/src/chain/ethereum.rs

+10-89
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
11
use {
22
crate::{
33
api::ChainId,
4-
chain::{
4+
chain::reader::{
5+
self, BlockNumber, BlockStatus, EntropyReader, RequestedWithCallbackEvent,
6+
},
7+
config::EthereumConfig,
8+
eth_utils::{
59
eth_gas_oracle::EthProviderOracle,
10+
legacy_tx_middleware::LegacyTxMiddleware,
611
nonce_manager::NonceManagerMiddleware,
7-
reader::{self, BlockNumber, BlockStatus, EntropyReader, RequestedWithCallbackEvent},
812
traced_client::{RpcMetrics, TracedClient},
913
},
10-
config::EthereumConfig,
1114
},
1215
anyhow::{anyhow, Error, Result},
1316
axum::async_trait,
1417
ethers::{
1518
abi::RawLog,
16-
contract::{abigen, ContractCall, EthLogDecode},
19+
contract::{abigen, EthLogDecode},
1720
core::types::Address,
18-
middleware::{gas_oracle::GasOracleMiddleware, MiddlewareError, SignerMiddleware},
19-
prelude::{BlockId, JsonRpcClient, PendingTransaction, TransactionRequest},
21+
middleware::{gas_oracle::GasOracleMiddleware, SignerMiddleware},
22+
prelude::JsonRpcClient,
2023
providers::{Http, Middleware, Provider},
2124
signers::{LocalWallet, Signer},
22-
types::{transaction::eip2718::TypedTransaction, BlockNumber as EthersBlockNumber, U256},
25+
types::{BlockNumber as EthersBlockNumber, U256},
2326
},
2427
sha3::{Digest, Keccak256},
2528
std::sync::Arc,
26-
thiserror::Error,
2729
};
2830

2931
// TODO: Programmatically generate this so we don't have to keep committed ABI in sync with the
@@ -43,90 +45,9 @@ pub type SignablePythContractInner<T> = PythRandom<MiddlewaresWrapper<T>>;
4345
pub type SignablePythContract = SignablePythContractInner<Http>;
4446
pub type InstrumentedSignablePythContract = SignablePythContractInner<TracedClient>;
4547

46-
pub type PythContractCall = ContractCall<MiddlewaresWrapper<TracedClient>, ()>;
47-
4848
pub type PythContract = PythRandom<Provider<Http>>;
4949
pub type InstrumentedPythContract = PythRandom<Provider<TracedClient>>;
5050

51-
/// Middleware that converts a transaction into a legacy transaction if use_legacy_tx is true.
52-
/// We can not use TransformerMiddleware because keeper calls fill_transaction first which bypasses
53-
/// the transformer.
54-
#[derive(Clone, Debug)]
55-
pub struct LegacyTxMiddleware<M> {
56-
use_legacy_tx: bool,
57-
inner: M,
58-
}
59-
60-
impl<M> LegacyTxMiddleware<M> {
61-
pub fn new(use_legacy_tx: bool, inner: M) -> Self {
62-
Self {
63-
use_legacy_tx,
64-
inner,
65-
}
66-
}
67-
}
68-
69-
#[derive(Error, Debug)]
70-
pub enum LegacyTxMiddlewareError<M: Middleware> {
71-
#[error("{0}")]
72-
MiddlewareError(M::Error),
73-
}
74-
75-
impl<M: Middleware> MiddlewareError for LegacyTxMiddlewareError<M> {
76-
type Inner = M::Error;
77-
78-
fn from_err(src: M::Error) -> Self {
79-
LegacyTxMiddlewareError::MiddlewareError(src)
80-
}
81-
82-
fn as_inner(&self) -> Option<&Self::Inner> {
83-
match self {
84-
LegacyTxMiddlewareError::MiddlewareError(e) => Some(e),
85-
}
86-
}
87-
}
88-
89-
#[async_trait]
90-
impl<M: Middleware> Middleware for LegacyTxMiddleware<M> {
91-
type Error = LegacyTxMiddlewareError<M>;
92-
type Provider = M::Provider;
93-
type Inner = M;
94-
fn inner(&self) -> &M {
95-
&self.inner
96-
}
97-
98-
async fn send_transaction<T: Into<TypedTransaction> + Send + Sync>(
99-
&self,
100-
tx: T,
101-
block: Option<BlockId>,
102-
) -> std::result::Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
103-
let mut tx = tx.into();
104-
if self.use_legacy_tx {
105-
let legacy_request: TransactionRequest = tx.into();
106-
tx = legacy_request.into();
107-
}
108-
self.inner()
109-
.send_transaction(tx, block)
110-
.await
111-
.map_err(MiddlewareError::from_err)
112-
}
113-
114-
async fn fill_transaction(
115-
&self,
116-
tx: &mut TypedTransaction,
117-
block: Option<BlockId>,
118-
) -> std::result::Result<(), Self::Error> {
119-
if self.use_legacy_tx {
120-
let legacy_request: TransactionRequest = (*tx).clone().into();
121-
*tx = legacy_request.into();
122-
}
123-
self.inner()
124-
.fill_transaction(tx, block)
125-
.await
126-
.map_err(MiddlewareError::from_err)
127-
}
128-
}
129-
13051
impl<T: JsonRpcClient + 'static + Clone> SignablePythContractInner<T> {
13152
/// Get the wallet that signs transactions sent to this contract.
13253
pub fn wallet(&self) -> LocalWallet {

apps/fortuna/src/command/run.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
use {
22
crate::{
33
api::{self, BlockchainState, ChainId},
4-
chain::{
5-
ethereum::InstrumentedPythContract,
6-
traced_client::{RpcMetrics, TracedClient},
7-
},
4+
chain::ethereum::InstrumentedPythContract,
85
command::register_provider::CommitmentMetadata,
96
config::{Commitment, Config, EthereumConfig, RunOptions},
10-
keeper::{self, KeeperMetrics},
7+
eth_utils::traced_client::{RpcMetrics, TracedClient},
8+
keeper::{self, keeper_metrics::KeeperMetrics},
119
state::{HashChainState, PebbleHashChain},
1210
},
1311
anyhow::{anyhow, Error, Result},

apps/fortuna/src/eth_utils.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub mod eth_gas_oracle;
2+
pub mod legacy_tx_middleware;
3+
pub mod nonce_manager;
4+
pub mod traced_client;
5+
pub mod utils;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use {
2+
axum::async_trait,
3+
ethers::{
4+
middleware::{Middleware, MiddlewareError},
5+
prelude::{BlockId, PendingTransaction, TransactionRequest},
6+
types::transaction::eip2718::TypedTransaction,
7+
},
8+
thiserror::Error,
9+
};
10+
11+
/// Middleware that converts a transaction into a legacy transaction if use_legacy_tx is true.
12+
/// We can not use TransformerMiddleware because keeper calls fill_transaction first which bypasses
13+
/// the transformer.
14+
#[derive(Clone, Debug)]
15+
pub struct LegacyTxMiddleware<M> {
16+
use_legacy_tx: bool,
17+
inner: M,
18+
}
19+
20+
impl<M> LegacyTxMiddleware<M> {
21+
pub fn new(use_legacy_tx: bool, inner: M) -> Self {
22+
Self {
23+
use_legacy_tx,
24+
inner,
25+
}
26+
}
27+
}
28+
29+
#[derive(Error, Debug)]
30+
pub enum LegacyTxMiddlewareError<M: Middleware> {
31+
#[error("{0}")]
32+
MiddlewareError(M::Error),
33+
}
34+
35+
impl<M: Middleware> MiddlewareError for LegacyTxMiddlewareError<M> {
36+
type Inner = M::Error;
37+
38+
fn from_err(src: M::Error) -> Self {
39+
LegacyTxMiddlewareError::MiddlewareError(src)
40+
}
41+
42+
fn as_inner(&self) -> Option<&Self::Inner> {
43+
match self {
44+
LegacyTxMiddlewareError::MiddlewareError(e) => Some(e),
45+
}
46+
}
47+
}
48+
49+
#[async_trait]
50+
impl<M: Middleware> Middleware for LegacyTxMiddleware<M> {
51+
type Error = LegacyTxMiddlewareError<M>;
52+
type Provider = M::Provider;
53+
type Inner = M;
54+
fn inner(&self) -> &M {
55+
&self.inner
56+
}
57+
58+
async fn send_transaction<T: Into<TypedTransaction> + Send + Sync>(
59+
&self,
60+
tx: T,
61+
block: Option<BlockId>,
62+
) -> std::result::Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
63+
let mut tx = tx.into();
64+
if self.use_legacy_tx {
65+
let legacy_request: TransactionRequest = tx.into();
66+
tx = legacy_request.into();
67+
}
68+
self.inner()
69+
.send_transaction(tx, block)
70+
.await
71+
.map_err(MiddlewareError::from_err)
72+
}
73+
74+
async fn fill_transaction(
75+
&self,
76+
tx: &mut TypedTransaction,
77+
block: Option<BlockId>,
78+
) -> std::result::Result<(), Self::Error> {
79+
if self.use_legacy_tx {
80+
let legacy_request: TransactionRequest = (*tx).clone().into();
81+
*tx = legacy_request.into();
82+
}
83+
self.inner()
84+
.fill_transaction(tx, block)
85+
.await
86+
.map_err(MiddlewareError::from_err)
87+
}
88+
}

apps/fortuna/src/eth_utils/utils.rs

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use {
2+
anyhow::{anyhow, Result},
3+
ethers::{contract::ContractCall, middleware::Middleware},
4+
std::sync::Arc,
5+
tracing,
6+
};
7+
8+
pub async fn send_and_confirm<A: Middleware>(contract_call: ContractCall<A, ()>) -> Result<()> {
9+
let call_name = contract_call.function.name.as_str();
10+
let pending_tx = contract_call
11+
.send()
12+
.await
13+
.map_err(|e| anyhow!("Error submitting transaction({}) {:?}", call_name, e))?;
14+
15+
let tx_result = pending_tx
16+
.await
17+
.map_err(|e| {
18+
anyhow!(
19+
"Error waiting for transaction({}) receipt: {:?}",
20+
call_name,
21+
e
22+
)
23+
})?
24+
.ok_or_else(|| {
25+
anyhow!(
26+
"Can't verify the transaction({}), probably dropped from mempool",
27+
call_name
28+
)
29+
})?;
30+
31+
tracing::info!(
32+
transaction_hash = &tx_result.transaction_hash.to_string(),
33+
"Confirmed transaction({}). Receipt: {:?}",
34+
call_name,
35+
tx_result,
36+
);
37+
Ok(())
38+
}
39+
40+
/// Estimate the cost (in wei) of a transaction consuming gas_used gas.
41+
pub async fn estimate_tx_cost<T: Middleware + 'static>(
42+
middleware: Arc<T>,
43+
use_legacy_tx: bool,
44+
gas_used: u128,
45+
) -> Result<u128> {
46+
let gas_price: u128 = if use_legacy_tx {
47+
middleware
48+
.get_gas_price()
49+
.await
50+
.map_err(|e| anyhow!("Failed to fetch gas price. error: {:?}", e))?
51+
.try_into()
52+
.map_err(|e| anyhow!("gas price doesn't fit into 128 bits. error: {:?}", e))?
53+
} else {
54+
// This is not obvious but the implementation of estimate_eip1559_fees in ethers.rs
55+
// for a middleware that has a GasOracleMiddleware inside is to ignore the passed-in callback
56+
// and use whatever the gas oracle returns.
57+
let (max_fee_per_gas, max_priority_fee_per_gas) =
58+
middleware.estimate_eip1559_fees(None).await?;
59+
60+
(max_fee_per_gas + max_priority_fee_per_gas)
61+
.try_into()
62+
.map_err(|e| anyhow!("gas price doesn't fit into 128 bits. error: {:?}", e))?
63+
};
64+
65+
Ok(gas_price * gas_used)
66+
}

0 commit comments

Comments
 (0)