use alloy::{
consensus::{
constants::GWEI_TO_WEI,
transaction::{Recovered, SignerRecoverable},
Signed, Transaction, TxEip1559, TxEnvelope,
},
eips::eip2718::Encodable2718,
network::TxSigner,
primitives::{Address, TxKind, U256},
rpc::types::mev::EthSendBundle,
signers::Signature,
};
use signet_bundle::SignetEthBundle;
use signet_test_utils::{
evm::test_sim_env,
test_constants::*,
users::{TEST_SIGNERS, TEST_USERS},
};
use tokio::time::{Duration, Instant};
#[tokio::test]
pub async fn successive_nonces() {
let builder = test_sim_env(Instant::now() + Duration::from_millis(200));
let sender = &TEST_SIGNERS[0];
let to = TEST_USERS[1];
for nonce in 0..4u64 {
let tx = signed_send_with_mfpg(
sender,
to,
U256::from(1000),
GWEI_TO_WEI as u128 * 10,
nonce.saturating_sub(1), )
.await;
builder.sim_items().add_tx(tx, 0);
}
let built = builder.build().await;
assert_eq!(built.transactions().len(), 3);
assert!(built.transactions().windows(2).all(|w| {
let tx1 = w[0].as_eip1559().unwrap().tx().nonce;
let tx2 = w[1].as_eip1559().unwrap().tx().nonce;
tx1 < tx2
}));
}
#[tokio::test(start_paused = true)]
pub async fn complex_simulation() {
let timeout = Duration::from_secs(10);
let builder = test_sim_env(Instant::now() + timeout);
for (i, sender) in TEST_SIGNERS.iter().enumerate() {
let tx = signed_send_with_mfpg(
sender,
TEST_USERS[i],
U256::from(1000),
(10 - i) as u128 * GWEI_TO_WEI as u128,
0,
)
.await;
builder.sim_items().add_tx(tx, 0);
}
let cache = builder.sim_items().clone();
let build_task = tokio::spawn(async move { builder.build().await });
let wait_for_empty_cache = async {
loop {
tokio::task::yield_now().await;
if cache.is_empty() {
break;
}
tokio::time::advance(Duration::from_micros(1)).await;
}
};
tokio::time::timeout(timeout, wait_for_empty_cache)
.await
.expect("timed out waiting for empty cache");
tokio::time::advance(timeout).await;
let built = build_task.await.unwrap();
assert_eq!(built.transactions().len(), 10);
assert!(built.transactions().windows(2).all(|w| {
let tx1 = w[0].as_eip1559().unwrap().tx().max_priority_fee_per_gas;
let tx2 = w[1].as_eip1559().unwrap().tx().max_priority_fee_per_gas;
tx1 >= tx2
}));
}
#[tokio::test]
async fn test_bundle_future_validity() {
signet_test_utils::init_tracing();
let builder = test_sim_env(Instant::now() + Duration::from_millis(200));
let sender_0 = &TEST_SIGNERS[0];
let sender_1 = &TEST_SIGNERS[1];
let to = TEST_USERS[2];
let bare_tx =
signed_send_with_mfpg(sender_0, to, U256::from(1000), GWEI_TO_WEI as u128 * 10, 0).await;
let bundle_tx_0 =
signed_send_with_mfpg(sender_0, to, U256::from(1000), GWEI_TO_WEI as u128 * 10, 1)
.await
.encoded_2718()
.into();
let bundle_tx_1 =
signed_send_with_mfpg(sender_1, to, U256::from(1000), GWEI_TO_WEI as u128 * 10, 0)
.await
.encoded_2718()
.into();
let bundle = SignetEthBundle {
bundle: EthSendBundle {
txs: vec![bundle_tx_0, bundle_tx_1],
replacement_uuid: Some(Default::default()),
..Default::default()
},
host_txs: vec![],
};
builder.sim_items().add_bundle(bundle, 0).unwrap();
let cache = builder.sim_items().clone();
let build_task = tokio::spawn(async move { builder.build().await });
tokio::time::sleep(Duration::from_millis(50)).await;
cache.add_tx(bare_tx, 0);
let built = build_task.await.unwrap();
assert_eq!(built.transactions().len(), 3);
assert_eq!(built.transactions()[0].nonce(), 0);
assert_eq!(built.transactions()[1].nonce(), 1);
assert_eq!(built.transactions()[2].nonce(), 0);
assert_eq!(built.transactions()[0].signer(), built.transactions()[1].signer());
}
fn send_with_mfpg(to: Address, value: U256, mpfpg: u128, nonce: u64) -> TxEip1559 {
TxEip1559 {
nonce,
gas_limit: 21_000,
to: TxKind::Call(to),
value,
chain_id: RU_CHAIN_ID,
max_fee_per_gas: GWEI_TO_WEI as u128 * 100,
max_priority_fee_per_gas: mpfpg,
..Default::default()
}
}
async fn signed_send_with_mfpg<S: TxSigner<Signature>>(
from: S,
to: Address,
value: U256,
mpfpg: u128,
nonce: u64,
) -> Recovered<TxEnvelope> {
let mut tx = send_with_mfpg(to, value, mpfpg, nonce);
let res = from.sign_transaction(&mut tx).await.unwrap();
TxEnvelope::from(Signed::new_unhashed(tx, res)).try_into_recovered().unwrap()
}