Skip to content
This repository was archived by the owner on Oct 19, 2024. It is now read-only.

Commit 69f22c4

Browse files
committed
feat: support eth_sendRawTransactionConditional
1 parent 5394d89 commit 69f22c4

File tree

7 files changed

+124
-7
lines changed

7 files changed

+124
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use crate::types::{Address, BlockNumber, H256};
2+
use serde::{Deserialize, Serialize};
3+
use std::collections::HashMap;
4+
5+
/// Extra options parameter for `eth_sendRawTransactionConditional`
6+
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
7+
pub struct ConditionalOptions {
8+
/// A map of accounts with expected storage
9+
#[serde(rename = "knownAccounts")]
10+
pub known_accounts: HashMap<Address, AccountStorage>,
11+
12+
/// Minimal block number for inclusion
13+
#[serde(rename = "blockNumberMin", skip_serializing_if = "Option::is_none")]
14+
pub block_number_min: Option<BlockNumber>,
15+
16+
/// Maximum block number for inclusion
17+
#[serde(rename = "blockNumberMax", skip_serializing_if = "Option::is_none")]
18+
pub block_number_max: Option<BlockNumber>,
19+
20+
/// Minimal block timestamp for inclusion
21+
#[serde(rename = "timestampMin", skip_serializing_if = "Option::is_none")]
22+
pub timestamp_min: Option<u64>,
23+
24+
/// Maximum block timestamp for inclusion
25+
#[serde(rename = "timestampMax", skip_serializing_if = "Option::is_none")]
26+
pub timestamp_max: Option<u64>,
27+
}
28+
29+
/// Account storage
30+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31+
#[serde(untagged)]
32+
pub enum AccountStorage {
33+
RootHash(H256),
34+
SlotValues(HashMap<String, String>),
35+
}

ethers-core/src/types/transaction/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ pub mod eip1559;
55
pub mod eip2718;
66
pub mod eip2930;
77

8+
pub mod conditional;
9+
810
#[cfg(feature = "optimism")]
911
pub mod optimism;
1012

ethers-middleware/src/signer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ where
132132
/// If the transaction does not have a chain id set, it sets it to the signer's chain id.
133133
/// Returns an error if the transaction's existing chain id does not match the signer's chain
134134
/// id.
135-
async fn sign_transaction(
135+
pub async fn sign_transaction(
136136
&self,
137137
mut tx: TypedTransaction,
138138
) -> Result<Bytes, SignerMiddlewareError<M, S>> {

ethers-providers/src/middleware.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use async_trait::async_trait;
22
use auto_impl::auto_impl;
33
use ethers_core::types::{
4-
transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed},
4+
transaction::{
5+
conditional::ConditionalOptions, eip2718::TypedTransaction, eip2930::AccessListWithGasUsed,
6+
},
57
*,
68
};
79
use futures_util::future::join_all;
@@ -460,6 +462,20 @@ pub trait Middleware: Sync + Send + Debug {
460462
self.inner().send_raw_transaction(tx).await.map_err(MiddlewareError::from_err)
461463
}
462464

465+
/// Send the raw RLP encoded transaction to the entire Ethereum network with conditional options
466+
/// and returns the transaction's hash This will consume gas from the account that signed the
467+
/// transaction. <https://notes.ethereum.org/@yoav/SkaX2lS9j#>
468+
async fn send_raw_transaction_conditional<'a>(
469+
&'a self,
470+
tx: Bytes,
471+
options: ConditionalOptions,
472+
) -> Result<PendingTransaction<'a, Self::Provider>, Self::Error> {
473+
self.inner()
474+
.send_raw_transaction_conditional(tx, options)
475+
.await
476+
.map_err(MiddlewareError::from_err)
477+
}
478+
463479
/// This returns true if either the middleware stack contains a `SignerMiddleware`, or the
464480
/// JSON-RPC provider has an unlocked key that can sign using the `eth_sign` call. If none of
465481
/// the above conditions are met, then the middleware stack is not capable of signing data.

ethers-providers/src/rpc/provider.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use ethers_core::types::SyncingStatus;
2-
31
use crate::{
42
call_raw::CallBuilder,
53
errors::ProviderError,
@@ -22,11 +20,14 @@ use async_trait::async_trait;
2220
use ethers_core::{
2321
abi::{self, Detokenize, ParamType},
2422
types::{
25-
transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed},
23+
transaction::{
24+
conditional::ConditionalOptions, eip2718::TypedTransaction,
25+
eip2930::AccessListWithGasUsed,
26+
},
2627
Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, Chain, EIP1186ProofResponse,
2728
FeeHistory, Filter, FilterBlockOption, GethDebugTracingCallOptions,
28-
GethDebugTracingOptions, GethTrace, Log, NameOrAddress, Selector, Signature, Trace,
29-
TraceFilter, TraceType, Transaction, TransactionReceipt, TransactionRequest, TxHash,
29+
GethDebugTracingOptions, GethTrace, Log, NameOrAddress, Selector, Signature, SyncingStatus,
30+
Trace, TraceFilter, TraceType, Transaction, TransactionReceipt, TransactionRequest, TxHash,
3031
TxpoolContent, TxpoolInspect, TxpoolStatus, H256, U256, U64,
3132
},
3233
utils,
@@ -581,6 +582,17 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
581582
Ok(PendingTransaction::new(tx_hash, self))
582583
}
583584

585+
async fn send_raw_transaction_conditional<'a>(
586+
&'a self,
587+
tx: Bytes,
588+
options: ConditionalOptions,
589+
) -> Result<PendingTransaction<'a, P>, ProviderError> {
590+
let rlp = utils::serialize(&tx);
591+
let options = utils::serialize(&options);
592+
let tx_hash = self.request("eth_sendRawTransactionConditional", [rlp, options]).await?;
593+
Ok(PendingTransaction::new(tx_hash, self))
594+
}
595+
584596
async fn is_signer(&self) -> bool {
585597
match self.from {
586598
Some(sender) => self.sign(vec![], &sender).await.is_ok(),

examples/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
- [x] Remove liquidity
6767
- [ ] Set gas for a transaction
6868
- [ ] Send raw transaction
69+
- [x] Send raw transaction conditional
6970
- [ ] Send typed transaction
7071
- [x] Trace
7172
- [ ] Transaction receipt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use ethers::{
2+
middleware::SignerMiddleware,
3+
providers::{Http, Middleware, Provider},
4+
signers::{LocalWallet, Signer},
5+
types::{transaction::conditional::ConditionalOptions, BlockNumber, TransactionRequest},
6+
};
7+
use eyre::Result;
8+
9+
/// Use 'eth_sendRawTransactionConditional' to send a transaction with a conditional options
10+
/// requires, a valid endpoint in `RPC_URL` env var that supports
11+
/// `eth_sendRawTransactionConditional`
12+
#[tokio::main]
13+
async fn main() -> Result<()> {
14+
if let Ok(url) = std::env::var("RPC_URL") {
15+
let provider = Provider::<Http>::try_from(url)?;
16+
let chain_id = provider.get_chainid().await?;
17+
let wallet: LocalWallet =
18+
"380eb0f3d505f087e438eca80bc4df9a7faa24f868e69fc0440261a0fc0567dc".parse()?;
19+
let from = wallet.address();
20+
21+
let client = SignerMiddleware::new(provider, wallet.with_chain_id(chain_id.as_u64()));
22+
23+
let mut tx = TransactionRequest::default()
24+
.from(from)
25+
.to("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
26+
.value(100)
27+
.into();
28+
29+
client.fill_transaction(&mut tx, None).await.unwrap();
30+
31+
let signed_tx = client.sign_transaction(tx).await.unwrap();
32+
let pending_tx = client
33+
.send_raw_transaction_conditional(
34+
signed_tx,
35+
ConditionalOptions {
36+
block_number_min: Some(BlockNumber::from(33285900)),
37+
..Default::default()
38+
},
39+
)
40+
.await
41+
.unwrap();
42+
43+
let receipt = pending_tx.await?.ok_or_else(|| eyre::eyre!("tx not included"))?;
44+
let tx = client.get_transaction(receipt.transaction_hash).await?;
45+
46+
println!("Sent transaction: {}\n", serde_json::to_string(&tx)?);
47+
println!("Receipt: {}\n", serde_json::to_string(&receipt)?);
48+
}
49+
50+
Ok(())
51+
}

0 commit comments

Comments
 (0)