Skip to content

Example: delay_layer

To run this example:

  • Clone the examples repository: git clone git@github.com:alloy-rs/examples.git
  • Run: cargo run --example delay_layer
//! Demonstrates how to implement a custom transport layer that delays dispatching the requests.
 
use eyre::Result;
use std::{
    task::{Context, Poll},
    time::Duration,
};
 
use alloy::{
    network::TransactionBuilder,
    node_bindings::Anvil,
    primitives::{Address, U256},
    providers::{Provider, ProviderBuilder},
    rpc::{client::ClientBuilder, types::TransactionRequest},
    signers::local::PrivateKeySigner,
    transports::BoxFuture,
};
use tokio::time::sleep;
use tower::{Layer, Service};
 
/// A [`tower::Service`] that delays the dispatch of requests by a specified duration.
#[derive(Debug, Clone)]
pub struct DelayService<S> {
    service: S,
    delay: Duration,
}
 
/// A [`tower::Layer`] that returns a new [`DelayService`] with the specified delay.
#[derive(Debug, Clone)]
pub struct DelayLayer {
    delay: Duration,
}
 
impl DelayLayer {
    /// Creates a new [`DelayLayer`] with the specified delay.
    pub fn new(delay: Duration) -> Self {
        Self { delay }
    }
}
 
impl<S> Layer<S> for DelayLayer {
    type Service = DelayService<S>;
 
    fn layer(&self, service: S) -> Self::Service {
        DelayService { service, delay: self.delay }
    }
}
 
/// Implement the [`tower::Service`] trait for the [`DelayService`].
impl<S, Request> Service<Request> for DelayService<S>
where
    S: Service<Request> + Send,
    S::Future: Send + 'static,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
 
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }
 
    fn call(&mut self, req: Request) -> Self::Future {
        let delay = self.delay;
        let future = self.service.call(req);
 
        Box::pin(async move {
            println!("Delaying for {} seconds...", delay.as_secs());
            sleep(delay).await;
            println!("Dispatching request...");
            future.await
        })
    }
}
 
#[tokio::main]
async fn main() -> Result<()> {
    let anvil = Anvil::new().try_spawn()?;
    let signer: PrivateKeySigner = anvil.keys()[0].clone().into();
 
    // Build a RPC client with the `DelayLayer`.
    let client = ClientBuilder::default()
        .layer(DelayLayer::new(Duration::from_secs(1)))
        .http(anvil.endpoint().parse()?);
 
    // Instatiate a provider with the RPC-client that uses the `DelayLayer`.
    let provider = ProviderBuilder::new().wallet(signer).on_client(client);
 
    let bob = Address::from([0x42; 20]);
    let tx = TransactionRequest::default().with_to(bob).with_value(U256::from(1));
 
    let bob_balance_before = provider.get_balance(bob).await?;
    let receipt = provider.send_transaction(tx).await?.get_receipt().await?;
    assert!(receipt.status(), "Transaction failed");
    let bob_balance_after = provider.get_balance(bob).await?;
    println!("Balance before: {}\nBalance after: {}", bob_balance_before, bob_balance_after);
 
    Ok(())
}

Find the source code on Github here.