alloy_network/ethereum/
wallet.rs1use crate::{AnyNetwork, AnyTxEnvelope, AnyTypedTransaction, Network, NetworkWallet, TxSigner};
2use alloy_consensus::{SignableTransaction, TxEnvelope, TypedTransaction};
3use alloy_primitives::{map::AddressHashMap, Address, PrimitiveSignature as 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 default_signer(&self) -> Arc<dyn TxSigner<Signature> + Send + Sync + 'static> {
72 self.signers.get(&self.default).cloned().expect("invalid signer")
73 }
74
75 pub fn signer_by_address(
77 &self,
78 address: Address,
79 ) -> Option<Arc<dyn TxSigner<Signature> + Send + Sync + 'static>> {
80 self.signers.get(&address).cloned()
81 }
82
83 #[doc(alias = "sign_tx_inner")]
84 async fn sign_transaction_inner(
85 &self,
86 sender: Address,
87 tx: &mut dyn SignableTransaction<Signature>,
88 ) -> alloy_signer::Result<Signature> {
89 self.signer_by_address(sender)
90 .ok_or_else(|| {
91 alloy_signer::Error::other(format!("Missing signing credential for {}", sender))
92 })?
93 .sign_transaction(tx)
94 .await
95 }
96}
97
98impl<N> NetworkWallet<N> for EthereumWallet
99where
100 N: Network<UnsignedTx = TypedTransaction, TxEnvelope = TxEnvelope>,
101{
102 fn default_signer_address(&self) -> Address {
103 self.default
104 }
105
106 fn has_signer_for(&self, address: &Address) -> bool {
107 self.signers.contains_key(address)
108 }
109
110 fn signer_addresses(&self) -> impl Iterator<Item = Address> {
111 self.signers.keys().copied()
112 }
113
114 #[doc(alias = "sign_tx_from")]
115 async fn sign_transaction_from(
116 &self,
117 sender: Address,
118 tx: TypedTransaction,
119 ) -> alloy_signer::Result<TxEnvelope> {
120 match tx {
121 TypedTransaction::Legacy(mut t) => {
122 let sig = self.sign_transaction_inner(sender, &mut t).await?;
123 Ok(t.into_signed(sig).into())
124 }
125 TypedTransaction::Eip2930(mut t) => {
126 let sig = self.sign_transaction_inner(sender, &mut t).await?;
127 Ok(t.into_signed(sig).into())
128 }
129 TypedTransaction::Eip1559(mut t) => {
130 let sig = self.sign_transaction_inner(sender, &mut t).await?;
131 Ok(t.into_signed(sig).into())
132 }
133 TypedTransaction::Eip4844(mut t) => {
134 let sig = self.sign_transaction_inner(sender, &mut t).await?;
135 Ok(t.into_signed(sig).into())
136 }
137 TypedTransaction::Eip7702(mut t) => {
138 let sig = self.sign_transaction_inner(sender, &mut t).await?;
139 Ok(t.into_signed(sig).into())
140 }
141 }
142 }
143}
144
145impl NetworkWallet<AnyNetwork> for EthereumWallet {
146 fn default_signer_address(&self) -> Address {
147 self.default
148 }
149
150 fn has_signer_for(&self, address: &Address) -> bool {
151 self.signers.contains_key(address)
152 }
153
154 fn signer_addresses(&self) -> impl Iterator<Item = Address> {
155 self.signers.keys().copied()
156 }
157
158 #[doc(alias = "sign_tx_from")]
159 async fn sign_transaction_from(
160 &self,
161 sender: Address,
162 tx: AnyTypedTransaction,
163 ) -> alloy_signer::Result<AnyTxEnvelope> {
164 match tx {
165 AnyTypedTransaction::Ethereum(t) => Ok(AnyTxEnvelope::Ethereum(
166 NetworkWallet::<Ethereum>::sign_transaction_from(self, sender, t).await?,
167 )),
168 _ => Err(alloy_signer::Error::other("cannot sign UnknownTypedTransaction")),
169 }
170 }
171}
172
173pub trait IntoWallet<N: Network = Ethereum>: Send + Sync + Debug {
175 type NetworkWallet: NetworkWallet<N>;
177 fn into_wallet(self) -> Self::NetworkWallet;
179}
180
181impl<W: NetworkWallet<N>, N: Network> IntoWallet<N> for W {
182 type NetworkWallet = W;
183
184 fn into_wallet(self) -> Self::NetworkWallet {
185 self
186 }
187}