Skip to content

Commit

Permalink
feature: WalletProvider (#569)
Browse files Browse the repository at this point in the history
* feature: WalletProvider

* feature: signer_mut

* test: a basic one

* test: bubbles up

* refactor: renames for clarity
  • Loading branch information
prestwich authored Apr 18, 2024
1 parent 955687c commit c657042
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 11 deletions.
6 changes: 3 additions & 3 deletions crates/network/src/ethereum/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,15 @@ impl<N> NetworkSigner<N> for EthereumSigner
where
N: Network<UnsignedTx = TypedTransaction, TxEnvelope = TxEnvelope>,
{
fn default_signer(&self) -> Address {
fn default_signer_address(&self) -> Address {
self.default
}

fn is_signer_for(&self, address: &Address) -> bool {
fn has_signer_for(&self, address: &Address) -> bool {
self.secp_signers.contains_key(address)
}

fn signers(&self) -> impl Iterator<Item = Address> {
fn signer_addresses(&self) -> impl Iterator<Item = Address> {
self.secp_signers.keys().copied()
}

Expand Down
10 changes: 5 additions & 5 deletions crates/network/src/transaction/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ pub trait NetworkSigner<N: Network>: std::fmt::Debug + Send + Sync {
/// Get the default signer address. This address should be used
/// in [`NetworkSigner::sign_transaction_from`] when no specific signer is
/// specified.
fn default_signer(&self) -> Address;
fn default_signer_address(&self) -> Address;

/// Return true if the signer contains a credential for the given address.
fn is_signer_for(&self, address: &Address) -> bool;
fn has_signer_for(&self, address: &Address) -> bool;

/// Return an iterator of all signer addresses.
fn signers(&self) -> impl Iterator<Item = Address>;
fn signer_addresses(&self) -> impl Iterator<Item = Address>;

/// Asynchronously sign an unsigned transaction, with a specified
/// credential.
Expand All @@ -41,7 +41,7 @@ pub trait NetworkSigner<N: Network>: std::fmt::Debug + Send + Sync {
&self,
tx: N::UnsignedTx,
) -> impl_future!(<Output = alloy_signer::Result<N::TxEnvelope>>) {
self.sign_transaction_from(self.default_signer(), tx)
self.sign_transaction_from(self.default_signer_address(), tx)
}

/// Asynchronously sign a transaction request, using the sender specified
Expand All @@ -50,7 +50,7 @@ pub trait NetworkSigner<N: Network>: std::fmt::Debug + Send + Sync {
&self,
request: N::TransactionRequest,
) -> alloy_signer::Result<N::TxEnvelope> {
let sender = request.from().unwrap_or_else(|| self.default_signer());
let sender = request.from().unwrap_or_else(|| self.default_signer_address());
let tx = request.build_unsigned().map_err(|(_, e)| alloy_signer::Error::other(e))?;
self.sign_transaction_from(sender, tx).await
}
Expand Down
17 changes: 17 additions & 0 deletions crates/provider/src/fillers/join_fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,23 @@ impl<L, R> JoinFill<L, R> {
pub const fn new(left: L, right: R) -> Self {
Self { left, right }
}

/// Get a reference to the left filler.
pub const fn left(&self) -> &L {
&self.left
}

/// Get a reference to the right filler.
pub const fn right(&self) -> &R {
&self.right
}

/// Get a mutable reference to the left filler.
///
/// NB: this function exists to enable the [`crate::WalletProvider`] impl.
pub(crate) fn right_mut(&mut self) -> &mut R {
&mut self.right
}
}

impl<L, R> JoinFill<L, R> {
Expand Down
4 changes: 2 additions & 2 deletions crates/provider/src/fillers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ where
T: Transport + Clone,
N: Network,
{
inner: P,
filler: F,
pub(crate) inner: P,
pub(crate) filler: F,
_pd: PhantomData<fn() -> (T, N)>,
}

Expand Down
14 changes: 13 additions & 1 deletion crates/provider/src/fillers/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ pub struct SignerFiller<S> {
signer: S,
}

impl<S> AsRef<S> for SignerFiller<S> {
fn as_ref(&self) -> &S {
&self.signer
}
}

impl<S> AsMut<S> for SignerFiller<S> {
fn as_mut(&mut self) -> &mut S {
&mut self.signer
}
}

impl<S> SignerFiller<S> {
/// Creates a new signing layer with the given signer.
pub const fn new(signer: S) -> Self {
Expand Down Expand Up @@ -82,7 +94,7 @@ where
};

if builder.from().is_none() {
builder.set_from(self.signer.default_signer());
builder.set_from(self.signer.default_signer_address());
if !builder.can_build() {
return Ok(SendableTx::Builder(builder));
}
Expand Down
3 changes: 3 additions & 0 deletions crates/provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ pub use heart::{PendingTransaction, PendingTransactionBuilder, PendingTransactio
mod provider;
pub use provider::{FilterPollerBuilder, Provider, RootProvider};

mod wallet;
pub use wallet::WalletProvider;

pub mod admin;
pub mod debug;
pub mod txpool;
Expand Down
113 changes: 113 additions & 0 deletions crates/provider/src/wallet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use crate::{
fillers::{FillProvider, JoinFill, SignerFiller, TxFiller},
Provider,
};
use alloy_network::{Ethereum, Network, NetworkSigner};
use alloy_primitives::Address;
use alloy_transport::Transport;

/// Trait for Providers, Fill stacks, etc, which contain [`NetworkSigner`].
pub trait WalletProvider<N: Network = Ethereum> {
/// The underlying [`NetworkSigner`] type contained in this stack.
type Signer: NetworkSigner<N>;

/// Get a reference to the underlying signer.
fn signer(&self) -> &Self::Signer;

/// Get a mutable reference to the underlying signer.
fn signer_mut(&mut self) -> &mut Self::Signer;

/// Get the default signer address.
fn default_signer_address(&self) -> Address {
self.signer().default_signer_address()
}

/// Check if the signer can sign for the given address.
fn has_signer_for(&self, address: &Address) -> bool {
self.signer().has_signer_for(address)
}

/// Get an iterator of all signer addresses. Note that because the signer
/// always has at least one address, this iterator will always have at least
/// one element.
fn signer_addresses(&self) -> impl Iterator<Item = Address> {
self.signer().signer_addresses()
}
}

impl<S, N> WalletProvider<N> for SignerFiller<S>
where
S: NetworkSigner<N> + Clone,
N: Network,
{
type Signer = S;

#[inline(always)]
fn signer(&self) -> &Self::Signer {
self.as_ref()
}

#[inline(always)]
fn signer_mut(&mut self) -> &mut Self::Signer {
self.as_mut()
}
}

impl<L, R, N> WalletProvider<N> for JoinFill<L, R>
where
R: WalletProvider<N>,
N: Network,
{
type Signer = R::Signer;

#[inline(always)]
fn signer(&self) -> &Self::Signer {
self.right().signer()
}

#[inline(always)]
fn signer_mut(&mut self) -> &mut Self::Signer {
self.right_mut().signer_mut()
}
}

impl<F, P, T, N> WalletProvider<N> for FillProvider<F, P, T, N>
where
F: TxFiller<N> + WalletProvider<N>,
P: Provider<T, N>,
T: Transport + Clone,
N: Network,
{
type Signer = F::Signer;

#[inline(always)]
fn signer(&self) -> &Self::Signer {
self.filler.signer()
}

#[inline(always)]
fn signer_mut(&mut self) -> &mut Self::Signer {
self.filler.signer_mut()
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::ProviderBuilder;

#[test]
fn basic_usage() {
let (provider, _anvil) = ProviderBuilder::new().on_anvil_with_signer();

assert_eq!(provider.default_signer_address(), provider.signer_addresses().next().unwrap());
}

#[test]
fn bubbles_through_fillers() {
let (provider, _anvil) =
ProviderBuilder::new().with_recommended_fillers().on_anvil_with_signer();

assert_eq!(provider.default_signer_address(), provider.signer_addresses().next().unwrap());
}
}

0 comments on commit c657042

Please sign in to comment.