alloy_network/ethereum/
wallet.rs1use crate::{Network, NetworkWallet, TxSigner};
2use alloy_consensus::{SignableTransaction, Signed};
3use alloy_primitives::{map::AddressHashMap, Address, Signature};
4use std::{fmt::Debug, sync::Arc};
5
6use super::Ethereum;
7
8#[derive(Clone, Default)]
10pub struct EthereumWallet {
11 default: Address,
12 signers: AddressHashMap<Arc<dyn TxSigner<Signature> + Send + Sync>>,
13}
14
15impl std::fmt::Debug for EthereumWallet {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 f.debug_struct("EthereumWallet")
18 .field("default_signer", &self.default)
19 .field("credentials", &self.signers.len())
20 .finish()
21 }
22}
23
24impl<S> From<S> for EthereumWallet
25where
26 S: TxSigner<Signature> + Send + Sync + 'static,
27{
28 fn from(signer: S) -> Self {
29 Self::new(signer)
30 }
31}
32
33impl EthereumWallet {
34 pub fn new<S>(signer: S) -> Self
36 where
37 S: TxSigner<Signature> + Send + Sync + 'static,
38 {
39 let mut this = Self::default();
40 this.register_default_signer(signer);
41 this
42 }
43
44 pub fn register_signer<S>(&mut self, signer: S)
51 where
52 S: TxSigner<Signature> + Send + Sync + 'static,
53 {
54 self.signers.insert(signer.address(), Arc::new(signer));
55 }
56
57 pub fn register_default_signer<S>(&mut self, signer: S)
65 where
66 S: TxSigner<Signature> + Send + Sync + 'static,
67 {
68 self.default = signer.address();
69 self.register_signer(signer);
70 }
71
72 pub fn set_default_signer(&mut self, address: Address) -> alloy_signer::Result<()> {
85 if self.signers.contains_key(&address) {
86 self.default = address;
87 Ok(())
88 } else {
89 Err(alloy_signer::Error::message(format!(
90 "{address} is not a registered signer. Use `register_default_signer`"
91 )))
92 }
93 }
94
95 pub fn default_signer(&self) -> Arc<dyn TxSigner<Signature> + Send + Sync + 'static> {
97 self.signers.get(&self.default).cloned().expect("invalid signer")
98 }
99
100 pub fn signer_by_address(
102 &self,
103 address: Address,
104 ) -> Option<Arc<dyn TxSigner<Signature> + Send + Sync + 'static>> {
105 self.signers.get(&address).cloned()
106 }
107
108 #[doc(alias = "sign_tx_inner")]
109 async fn sign_transaction_inner(
110 &self,
111 sender: Address,
112 tx: &mut dyn SignableTransaction<Signature>,
113 ) -> alloy_signer::Result<Signature> {
114 self.signer_by_address(sender)
115 .ok_or_else(|| {
116 alloy_signer::Error::other(format!("Missing signing credential for {sender}"))
117 })?
118 .sign_transaction(tx)
119 .await
120 }
121}
122
123impl<N: Network> NetworkWallet<N> for EthereumWallet
124where
125 N::TxEnvelope: From<Signed<N::UnsignedTx>>,
126 N::UnsignedTx: SignableTransaction<Signature>,
127{
128 fn default_signer_address(&self) -> Address {
129 self.default
130 }
131
132 fn has_signer_for(&self, address: &Address) -> bool {
133 self.signers.contains_key(address)
134 }
135
136 fn signer_addresses(&self) -> impl Iterator<Item = Address> {
137 self.signers.keys().copied()
138 }
139
140 async fn sign_transaction_from(
141 &self,
142 sender: Address,
143 mut tx: N::UnsignedTx,
144 ) -> alloy_signer::Result<N::TxEnvelope> {
145 let sig = self.sign_transaction_inner(sender, &mut tx).await?;
146 Ok(tx.into_signed(sig).into())
147 }
148}
149
150pub trait IntoWallet<N: Network = Ethereum>: Send + Sync + Debug {
152 type NetworkWallet: NetworkWallet<N>;
154 fn into_wallet(self) -> Self::NetworkWallet;
156}
157
158impl<W: NetworkWallet<N>, N: Network> IntoWallet<N> for W {
159 type NetworkWallet = W;
160
161 fn into_wallet(self) -> Self::NetworkWallet {
162 self
163 }
164}