alloy_network/ethereum/
wallet.rs1use crate::{AnyNetwork, AnyTxEnvelope, AnyTypedTransaction, Network, NetworkWallet, TxSigner};
2use alloy_consensus::{SignableTransaction, TxEnvelope, TypedTransaction};
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)
50 where
51 S: TxSigner<Signature> + Send + Sync + 'static,
52 {
53 self.signers.insert(signer.address(), Arc::new(signer));
54 }
55
56 pub fn register_default_signer<S>(&mut self, signer: S)
63 where
64 S: TxSigner<Signature> + Send + Sync + 'static,
65 {
66 self.default = signer.address();
67 self.register_signer(signer);
68 }
69
70 pub fn set_default_signer(&mut self, address: Address) -> alloy_signer::Result<()> {
82 if self.signers.contains_key(&address) {
83 self.default = address;
84 Ok(())
85 } else {
86 Err(alloy_signer::Error::message(format!(
87 "{address} is not a registered signer. Use `register_default_signer`"
88 )))
89 }
90 }
91
92 pub fn default_signer(&self) -> Arc<dyn TxSigner<Signature> + Send + Sync + 'static> {
94 self.signers.get(&self.default).cloned().expect("invalid signer")
95 }
96
97 pub fn signer_by_address(
99 &self,
100 address: Address,
101 ) -> Option<Arc<dyn TxSigner<Signature> + Send + Sync + 'static>> {
102 self.signers.get(&address).cloned()
103 }
104
105 #[doc(alias = "sign_tx_inner")]
106 async fn sign_transaction_inner(
107 &self,
108 sender: Address,
109 tx: &mut dyn SignableTransaction<Signature>,
110 ) -> alloy_signer::Result<Signature> {
111 self.signer_by_address(sender)
112 .ok_or_else(|| {
113 alloy_signer::Error::other(format!("Missing signing credential for {sender}"))
114 })?
115 .sign_transaction(tx)
116 .await
117 }
118}
119
120impl<N> NetworkWallet<N> for EthereumWallet
121where
122 N: Network<UnsignedTx = TypedTransaction, TxEnvelope = TxEnvelope>,
123{
124 fn default_signer_address(&self) -> Address {
125 self.default
126 }
127
128 fn has_signer_for(&self, address: &Address) -> bool {
129 self.signers.contains_key(address)
130 }
131
132 fn signer_addresses(&self) -> impl Iterator<Item = Address> {
133 self.signers.keys().copied()
134 }
135
136 #[doc(alias = "sign_tx_from")]
137 async fn sign_transaction_from(
138 &self,
139 sender: Address,
140 tx: TypedTransaction,
141 ) -> alloy_signer::Result<TxEnvelope> {
142 match tx {
143 TypedTransaction::Legacy(mut t) => {
144 let sig = self.sign_transaction_inner(sender, &mut t).await?;
145 Ok(t.into_signed(sig).into())
146 }
147 TypedTransaction::Eip2930(mut t) => {
148 let sig = self.sign_transaction_inner(sender, &mut t).await?;
149 Ok(t.into_signed(sig).into())
150 }
151 TypedTransaction::Eip1559(mut t) => {
152 let sig = self.sign_transaction_inner(sender, &mut t).await?;
153 Ok(t.into_signed(sig).into())
154 }
155 TypedTransaction::Eip4844(mut t) => {
156 let sig = self.sign_transaction_inner(sender, &mut t).await?;
157 Ok(t.into_signed(sig).into())
158 }
159 TypedTransaction::Eip7702(mut t) => {
160 let sig = self.sign_transaction_inner(sender, &mut t).await?;
161 Ok(t.into_signed(sig).into())
162 }
163 }
164 }
165}
166
167impl NetworkWallet<AnyNetwork> for EthereumWallet {
168 fn default_signer_address(&self) -> Address {
169 self.default
170 }
171
172 fn has_signer_for(&self, address: &Address) -> bool {
173 self.signers.contains_key(address)
174 }
175
176 fn signer_addresses(&self) -> impl Iterator<Item = Address> {
177 self.signers.keys().copied()
178 }
179
180 #[doc(alias = "sign_tx_from")]
181 async fn sign_transaction_from(
182 &self,
183 sender: Address,
184 tx: AnyTypedTransaction,
185 ) -> alloy_signer::Result<AnyTxEnvelope> {
186 match tx {
187 AnyTypedTransaction::Ethereum(t) => Ok(AnyTxEnvelope::Ethereum(
188 NetworkWallet::<Ethereum>::sign_transaction_from(self, sender, t).await?,
189 )),
190 _ => Err(alloy_signer::Error::other("cannot sign UnknownTypedTransaction")),
191 }
192 }
193}
194
195pub trait IntoWallet<N: Network = Ethereum>: Send + Sync + Debug {
197 type NetworkWallet: NetworkWallet<N>;
199 fn into_wallet(self) -> Self::NetworkWallet;
201}
202
203impl<W: NetworkWallet<N>, N: Network> IntoWallet<N> for W {
204 type NetworkWallet = W;
205
206 fn into_wallet(self) -> Self::NetworkWallet {
207 self
208 }
209}