anvil/eth/
sign.rs
1use crate::eth::error::BlockchainError;
2use alloy_consensus::SignableTransaction;
3use alloy_dyn_abi::TypedData;
4use alloy_network::TxSignerSync;
5use alloy_primitives::{map::AddressHashMap, Address, Signature, B256};
6use alloy_signer::Signer as AlloySigner;
7use alloy_signer_local::PrivateKeySigner;
8use anvil_core::eth::transaction::{TypedTransaction, TypedTransactionRequest};
9
10#[async_trait::async_trait]
12pub trait Signer: Send + Sync {
13 fn accounts(&self) -> Vec<Address>;
15
16 fn is_signer_for(&self, addr: Address) -> bool {
18 self.accounts().contains(&addr)
19 }
20
21 async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature, BlockchainError>;
23
24 async fn sign_typed_data(
27 &self,
28 address: Address,
29 payload: &TypedData,
30 ) -> Result<Signature, BlockchainError>;
31
32 async fn sign_hash(&self, address: Address, hash: B256) -> Result<Signature, BlockchainError>;
34
35 fn sign_transaction(
37 &self,
38 request: TypedTransactionRequest,
39 address: &Address,
40 ) -> Result<Signature, BlockchainError>;
41}
42
43pub struct DevSigner {
45 addresses: Vec<Address>,
46 accounts: AddressHashMap<PrivateKeySigner>,
47}
48
49impl DevSigner {
50 pub fn new(accounts: Vec<PrivateKeySigner>) -> Self {
51 let addresses = accounts.iter().map(|wallet| wallet.address()).collect::<Vec<_>>();
52 let accounts = addresses.iter().cloned().zip(accounts).collect();
53 Self { addresses, accounts }
54 }
55}
56
57#[async_trait::async_trait]
58impl Signer for DevSigner {
59 fn accounts(&self) -> Vec<Address> {
60 self.addresses.clone()
61 }
62
63 fn is_signer_for(&self, addr: Address) -> bool {
64 self.accounts.contains_key(&addr)
65 }
66
67 async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature, BlockchainError> {
68 let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?;
69
70 Ok(signer.sign_message(message).await?)
71 }
72
73 async fn sign_typed_data(
74 &self,
75 address: Address,
76 payload: &TypedData,
77 ) -> Result<Signature, BlockchainError> {
78 let mut signer =
79 self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?.to_owned();
80
81 signer.set_chain_id(None);
84
85 Ok(signer.sign_dynamic_typed_data(payload).await?)
86 }
87
88 async fn sign_hash(&self, address: Address, hash: B256) -> Result<Signature, BlockchainError> {
89 let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?;
90
91 Ok(signer.sign_hash(&hash).await?)
92 }
93
94 fn sign_transaction(
95 &self,
96 request: TypedTransactionRequest,
97 address: &Address,
98 ) -> Result<Signature, BlockchainError> {
99 let signer = self.accounts.get(address).ok_or(BlockchainError::NoSignerAvailable)?;
100 match request {
101 TypedTransactionRequest::Legacy(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
102 TypedTransactionRequest::EIP2930(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
103 TypedTransactionRequest::EIP1559(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
104 TypedTransactionRequest::EIP7702(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
105 TypedTransactionRequest::EIP4844(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
106 TypedTransactionRequest::Deposit(_) => {
107 unreachable!("op deposit txs should not be signed")
108 }
109 }
110 }
111}
112
113pub fn build_typed_transaction(
119 request: TypedTransactionRequest,
120 signature: Signature,
121) -> Result<TypedTransaction, BlockchainError> {
122 let tx = match request {
123 TypedTransactionRequest::Legacy(tx) => TypedTransaction::Legacy(tx.into_signed(signature)),
124 TypedTransactionRequest::EIP2930(tx) => {
125 TypedTransaction::EIP2930(tx.into_signed(signature))
126 }
127 TypedTransactionRequest::EIP1559(tx) => {
128 TypedTransaction::EIP1559(tx.into_signed(signature))
129 }
130 TypedTransactionRequest::EIP7702(tx) => {
131 TypedTransaction::EIP7702(tx.into_signed(signature))
132 }
133 TypedTransactionRequest::EIP4844(tx) => {
134 TypedTransaction::EIP4844(tx.into_signed(signature))
135 }
136 TypedTransactionRequest::Deposit(tx) => TypedTransaction::Deposit(tx),
137 };
138
139 Ok(tx)
140}