alloy_zksync/wallet/
mod.rs

1//! ZKsync wallet.
2
3use alloy::consensus::SignableTransaction;
4use alloy::network::{Network, NetworkWallet, TxSigner};
5use alloy::primitives::Address;
6
7use crate::network::{Zksync, tx_envelope::TxEnvelope, unsigned_tx::TypedTransaction};
8
9use alloy::primitives::Signature;
10use std::{collections::BTreeMap, sync::Arc};
11
12/// A wallet capable of signing any transaction for the Ethereum network.
13#[derive(Clone, Default)]
14pub struct ZksyncWallet {
15    default: Address,
16    signers: BTreeMap<Address, Arc<dyn TxSigner<Signature> + Send + Sync>>,
17}
18
19impl std::fmt::Debug for ZksyncWallet {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        f.debug_struct("ZksyncWallet")
22            .field("default_signer", &self.default)
23            .field("credentials", &self.signers.len())
24            .finish()
25    }
26}
27
28impl<S> From<S> for ZksyncWallet
29where
30    S: TxSigner<Signature> + Send + Sync + 'static,
31{
32    fn from(signer: S) -> Self {
33        Self::new(signer)
34    }
35}
36
37impl ZksyncWallet {
38    /// Create a new signer with the given signer as the default signer.
39    pub fn new<S>(signer: S) -> Self
40    where
41        S: TxSigner<Signature> + Send + Sync + 'static,
42    {
43        let mut this = Self::default();
44        this.register_default_signer(signer);
45        this
46    }
47
48    /// Register a new signer on this object. This signer will be used to sign
49    /// [`TransactionRequest`] and [`TypedTransaction`] object that specify the
50    /// signer's address in the `from` field.
51    ///
52    /// [`TransactionRequest`]: alloy::rpc::types::eth::TransactionRequest
53    pub fn register_signer<S>(&mut self, signer: S)
54    where
55        S: TxSigner<Signature> + Send + Sync + 'static,
56    {
57        self.signers.insert(signer.address(), Arc::new(signer));
58    }
59
60    /// Register a new signer on this object, and set it as the default signer.
61    /// This signer will be used to sign [`TransactionRequest`] and
62    /// [`TypedTransaction`] objects that do not specify a signer address in the
63    /// `from` field.
64    ///
65    /// [`TransactionRequest`]: alloy::rpc::types::eth::TransactionRequest
66    pub fn register_default_signer<S>(&mut self, signer: S)
67    where
68        S: TxSigner<Signature> + Send + Sync + 'static,
69    {
70        self.default = signer.address();
71        self.register_signer(signer);
72    }
73
74    /// Get the default signer.
75    pub fn default_signer(&self) -> Arc<dyn TxSigner<Signature> + Send + Sync + 'static> {
76        self.signers
77            .get(&self.default)
78            .cloned()
79            .expect("invalid signer")
80    }
81
82    /// Get the signer for the given address.
83    pub fn signer_by_address(
84        &self,
85        address: Address,
86    ) -> Option<Arc<dyn TxSigner<Signature> + Send + Sync + 'static>> {
87        self.signers.get(&address).cloned()
88    }
89
90    #[doc(alias = "sign_tx_inner")]
91    async fn sign_transaction_inner(
92        &self,
93        sender: Address,
94        tx: &mut dyn SignableTransaction<Signature>,
95    ) -> alloy::signers::Result<Signature> {
96        self.signer_by_address(sender)
97            .ok_or_else(|| {
98                alloy::signers::Error::other(format!("Missing signing credential for {sender}"))
99            })?
100            .sign_transaction(tx)
101            .await
102    }
103}
104
105impl<N> NetworkWallet<N> for ZksyncWallet
106where
107    N: Network<
108            UnsignedTx = alloy::consensus::TypedTransaction,
109            TxEnvelope = alloy::consensus::TxEnvelope,
110        >,
111{
112    fn default_signer_address(&self) -> Address {
113        self.default
114    }
115
116    fn has_signer_for(&self, address: &Address) -> bool {
117        self.signers.contains_key(address)
118    }
119
120    fn signer_addresses(&self) -> impl Iterator<Item = Address> {
121        self.signers.keys().copied()
122    }
123
124    #[doc(alias = "sign_tx_from")]
125    async fn sign_transaction_from(
126        &self,
127        sender: Address,
128        tx: alloy::consensus::TypedTransaction,
129    ) -> alloy::signers::Result<alloy::consensus::TxEnvelope> {
130        match tx {
131            alloy::consensus::TypedTransaction::Legacy(mut t) => {
132                let sig = self.sign_transaction_inner(sender, &mut t).await?;
133                Ok(t.into_signed(sig).into())
134            }
135            alloy::consensus::TypedTransaction::Eip2930(mut t) => {
136                let sig = self.sign_transaction_inner(sender, &mut t).await?;
137                Ok(t.into_signed(sig).into())
138            }
139            alloy::consensus::TypedTransaction::Eip1559(mut t) => {
140                let sig = self.sign_transaction_inner(sender, &mut t).await?;
141                Ok(t.into_signed(sig).into())
142            }
143            alloy::consensus::TypedTransaction::Eip4844(mut t) => {
144                let sig = self.sign_transaction_inner(sender, &mut t).await?;
145                Ok(t.into_signed(sig).into())
146            }
147            alloy::consensus::TypedTransaction::Eip7702(mut t) => {
148                let sig = self.sign_transaction_inner(sender, &mut t).await?;
149                Ok(t.into_signed(sig).into())
150            }
151        }
152    }
153}
154
155impl NetworkWallet<Zksync> for ZksyncWallet {
156    fn default_signer_address(&self) -> Address {
157        self.default
158    }
159
160    fn has_signer_for(&self, address: &Address) -> bool {
161        self.signers.contains_key(address)
162    }
163
164    fn signer_addresses(&self) -> impl Iterator<Item = Address> {
165        self.signers.keys().copied()
166    }
167
168    #[doc(alias = "sign_tx_from")]
169    async fn sign_transaction_from(
170        &self,
171        sender: Address,
172        tx: TypedTransaction,
173    ) -> alloy::signers::Result<TxEnvelope> {
174        match tx {
175            TypedTransaction::Native(t) => {
176                let sig = <Self as NetworkWallet<alloy::network::Ethereum>>::sign_transaction_from(
177                    self, sender, t,
178                )
179                .await?;
180                Ok(TxEnvelope::Native(sig))
181            }
182            TypedTransaction::Eip712(mut t) => {
183                let sig = self.sign_transaction_inner(sender, &mut t).await?;
184                Ok(TxEnvelope::Eip712(t.into_signed(sig)))
185            }
186        }
187    }
188}