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, PrimitiveSignature as Signature, B256, U256};
6use alloy_signer::Signer as AlloySigner;
7use alloy_signer_local::PrivateKeySigner;
8use anvil_core::eth::transaction::{
9 optimism::DepositTransaction, TypedTransaction, TypedTransactionRequest,
10};
11use op_alloy_consensus::TxDeposit;
12
13#[async_trait::async_trait]
15pub trait Signer: Send + Sync {
16 fn accounts(&self) -> Vec<Address>;
18
19 fn is_signer_for(&self, addr: Address) -> bool {
21 self.accounts().contains(&addr)
22 }
23
24 async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature, BlockchainError>;
26
27 async fn sign_typed_data(
30 &self,
31 address: Address,
32 payload: &TypedData,
33 ) -> Result<Signature, BlockchainError>;
34
35 async fn sign_hash(&self, address: Address, hash: B256) -> Result<Signature, BlockchainError>;
37
38 fn sign_transaction(
40 &self,
41 request: TypedTransactionRequest,
42 address: &Address,
43 ) -> Result<Signature, BlockchainError>;
44}
45
46pub struct DevSigner {
48 addresses: Vec<Address>,
49 accounts: AddressHashMap<PrivateKeySigner>,
50}
51
52impl DevSigner {
53 pub fn new(accounts: Vec<PrivateKeySigner>) -> Self {
54 let addresses = accounts.iter().map(|wallet| wallet.address()).collect::<Vec<_>>();
55 let accounts = addresses.iter().cloned().zip(accounts).collect();
56 Self { addresses, accounts }
57 }
58}
59
60#[async_trait::async_trait]
61impl Signer for DevSigner {
62 fn accounts(&self) -> Vec<Address> {
63 self.addresses.clone()
64 }
65
66 fn is_signer_for(&self, addr: Address) -> bool {
67 self.accounts.contains_key(&addr)
68 }
69
70 async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature, BlockchainError> {
71 let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?;
72
73 Ok(signer.sign_message(message).await?)
74 }
75
76 async fn sign_typed_data(
77 &self,
78 address: Address,
79 payload: &TypedData,
80 ) -> Result<Signature, BlockchainError> {
81 let mut signer =
82 self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?.to_owned();
83
84 signer.set_chain_id(None);
87
88 Ok(signer.sign_dynamic_typed_data(payload).await?)
89 }
90
91 async fn sign_hash(&self, address: Address, hash: B256) -> Result<Signature, BlockchainError> {
92 let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?;
93
94 Ok(signer.sign_hash(&hash).await?)
95 }
96
97 fn sign_transaction(
98 &self,
99 request: TypedTransactionRequest,
100 address: &Address,
101 ) -> Result<Signature, BlockchainError> {
102 let signer = self.accounts.get(address).ok_or(BlockchainError::NoSignerAvailable)?;
103 match request {
104 TypedTransactionRequest::Legacy(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
105 TypedTransactionRequest::EIP2930(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
106 TypedTransactionRequest::EIP1559(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
107 TypedTransactionRequest::EIP7702(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
108 TypedTransactionRequest::EIP4844(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
109 TypedTransactionRequest::Deposit(_) => {
110 unreachable!("op deposit txs should not be signed")
111 }
112 }
113 }
114}
115
116pub fn build_typed_transaction(
122 request: TypedTransactionRequest,
123 signature: Signature,
124) -> Result<TypedTransaction, BlockchainError> {
125 let tx = match request {
126 TypedTransactionRequest::Legacy(tx) => TypedTransaction::Legacy(tx.into_signed(signature)),
127 TypedTransactionRequest::EIP2930(tx) => {
128 TypedTransaction::EIP2930(tx.into_signed(signature))
129 }
130 TypedTransactionRequest::EIP1559(tx) => {
131 TypedTransaction::EIP1559(tx.into_signed(signature))
132 }
133 TypedTransactionRequest::EIP7702(tx) => {
134 TypedTransaction::EIP7702(tx.into_signed(signature))
135 }
136 TypedTransactionRequest::EIP4844(tx) => {
137 TypedTransaction::EIP4844(tx.into_signed(signature))
138 }
139 TypedTransactionRequest::Deposit(tx) => {
140 let TxDeposit {
141 from,
142 gas_limit,
143 to,
144 value,
145 input,
146 source_hash,
147 mint,
148 is_system_transaction,
149 ..
150 } = tx;
151 TypedTransaction::Deposit(DepositTransaction {
152 from,
153 gas_limit,
154 kind: to,
155 value,
156 input,
157 source_hash,
158 mint: mint.map_or(U256::ZERO, U256::from),
159 is_system_tx: is_system_transaction,
160 nonce: 0,
161 })
162 }
163 };
164
165 Ok(tx)
166}