1use std::path::{Path, PathBuf};
4
5use alloy::network::primitives::ReceiptResponse;
6use alloy::network::{AnyNetwork, Network};
7use alloy::primitives::{Address, Bytes, TxHash, U256};
8use alloy::providers::Provider;
9use alloy::signers::local::PrivateKeySigner;
10use alloy::sol_types::SolCall;
11
12use crate::account::Account;
13use crate::chain::{ChainAddresses, ChainConfig};
14use crate::contracts::{IMultiSend, IMultiSendCallOnly, ISafe};
15use crate::encoding::{compute_safe_transaction_hash, encode_multisend_data, SafeTxParams};
16use crate::error::{Error, Result};
17use crate::signing::sign_hash;
18use crate::simulation::{ForkSimulator, SimulationResult};
19use crate::types::{Call, CallBuilder, Operation};
20
21pub const SAFE_SINGLETON_SLOT: U256 = U256::ZERO;
25
26pub async fn is_safe<P: Provider<N>, N: Network>(
39 provider: &P,
40 address: Address,
41) -> Result<bool> {
42 let storage_value = provider
44 .get_storage_at(address, SAFE_SINGLETON_SLOT)
45 .await
46 .map_err(|e| Error::Fetch {
47 what: "singleton slot",
48 reason: e.to_string(),
49 })?;
50
51 let impl_address = Address::from_slice(&storage_value.to_be_bytes::<32>()[12..]);
53
54 let v1_4_1 = ChainAddresses::v1_4_1();
56 let v1_3_0 = ChainAddresses::v1_3_0();
57
58 Ok(impl_address == v1_4_1.safe_singleton || impl_address == v1_3_0.safe_singleton)
59}
60
61#[derive(Debug, Clone)]
63pub struct ExecutionResult {
64 pub tx_hash: TxHash,
66 pub success: bool,
68}
69
70pub struct Safe<P> {
72 provider: P,
74 signer: PrivateKeySigner,
76 address: Address,
78 config: ChainConfig,
80 debug_output_dir: Option<PathBuf>,
82}
83
84impl<P> Safe<P>
85where
86 P: Provider<AnyNetwork> + Clone + 'static,
87{
88 pub fn new(provider: P, signer: PrivateKeySigner, address: Address, config: ChainConfig) -> Self {
90 Self {
91 provider,
92 signer,
93 address,
94 config,
95 debug_output_dir: None,
96 }
97 }
98
99 pub fn with_debug_output_dir(mut self, path: impl Into<PathBuf>) -> Self {
104 self.debug_output_dir = Some(path.into());
105 self
106 }
107
108 pub async fn connect(provider: P, signer: PrivateKeySigner, address: Address) -> Result<Self> {
110 let chain_id = provider
111 .get_chain_id()
112 .await
113 .map_err(|e| Error::Provider(e.to_string()))?;
114
115 let config = ChainConfig::new(chain_id);
116 Ok(Self::new(provider, signer, address, config))
117 }
118
119 pub fn addresses(&self) -> &ChainAddresses {
121 &self.config.addresses
122 }
123
124 pub async fn threshold(&self) -> Result<u64> {
126 let safe = ISafe::new(self.address, &self.provider);
127 let threshold = safe
128 .getThreshold()
129 .call()
130 .await
131 .map_err(|e| Error::Fetch {
132 what: "threshold",
133 reason: e.to_string(),
134 })?;
135 Ok(threshold.to::<u64>())
136 }
137
138 pub async fn owners(&self) -> Result<Vec<Address>> {
140 let safe = ISafe::new(self.address, &self.provider);
141 let owners = safe
142 .getOwners()
143 .call()
144 .await
145 .map_err(|e| Error::Fetch {
146 what: "owners",
147 reason: e.to_string(),
148 })?;
149 Ok(owners)
150 }
151
152 pub async fn is_owner(&self, address: Address) -> Result<bool> {
154 let safe = ISafe::new(self.address, &self.provider);
155 let is_owner = safe
156 .isOwner(address)
157 .call()
158 .await
159 .map_err(|e| Error::Fetch {
160 what: "is_owner",
161 reason: e.to_string(),
162 })?;
163 Ok(is_owner)
164 }
165
166 pub async fn verify_single_owner(&self) -> Result<()> {
168 let threshold = self.threshold().await?;
169 if threshold != 1 {
170 return Err(Error::InvalidThreshold { threshold });
171 }
172
173 let is_owner = self.is_owner(self.signer.address()).await?;
174 if !is_owner {
175 return Err(Error::NotOwner {
176 signer: self.signer.address(),
177 safe: self.address,
178 });
179 }
180
181 Ok(())
182 }
183}
184
185pub struct SafeBuilder<'a, P> {
187 safe: &'a Safe<P>,
188 calls: Vec<Call>,
189 use_call_only: bool,
190 safe_tx_gas: Option<U256>,
191 operation: Operation,
192 simulation_result: Option<SimulationResult>,
193}
194
195impl<'a, P> SafeBuilder<'a, P>
196where
197 P: Provider<AnyNetwork> + Clone + 'static,
198{
199 fn new(safe: &'a Safe<P>) -> Self {
200 SafeBuilder {
201 safe,
202 calls: Vec::new(),
203 use_call_only: false,
204 safe_tx_gas: None,
205 operation: Operation::DelegateCall, simulation_result: None,
207 }
208 }
209
210 pub fn call_only(mut self) -> Self {
212 self.use_call_only = true;
213 self
214 }
215
216 pub fn with_operation(mut self, operation: Operation) -> Self {
218 self.operation = operation;
219 self
220 }
221
222 pub fn with_safe_tx_gas(mut self, gas: U256) -> Self {
224 self.safe_tx_gas = Some(gas);
225 self
226 }
227
228 pub fn with_gas_limit(mut self, gas_limit: u64) -> Self {
232 self.safe_tx_gas = Some(U256::from(gas_limit));
233 self
234 }
235
236 pub async fn simulate(mut self) -> Result<Self> {
245 if self.calls.is_empty() {
246 return Err(Error::NoCalls);
247 }
248
249 let (to, value, data, operation) = self.build_call_params()?;
250
251 let mut simulator = ForkSimulator::new(self.safe.provider.clone(), self.safe.config.chain_id);
252
253 if let Some(dir) = &self.safe.debug_output_dir {
255 simulator = simulator.with_debug_output_dir(dir.clone(), self.safe.address);
256 }
257
258 let result = match operation {
262 Operation::DelegateCall => {
263 self.simulate_via_exec_transaction(&simulator, to, value, data, operation)
265 .await?
266 }
267 Operation::Call => {
268 simulator
269 .simulate_call(self.safe.address, to, value, data, operation)
270 .await?
271 }
272 };
273
274 self.simulation_result = Some(result);
276 Ok(self)
277 }
278
279 pub fn simulation_success(self) -> Result<Self> {
294 match &self.simulation_result {
295 None => Err(Error::SimulationNotPerformed),
296 Some(result) if !result.success => Err(Error::SimulationReverted {
297 reason: result
298 .revert_reason
299 .clone()
300 .unwrap_or_else(|| "Unknown".to_string()),
301 }),
302 Some(_) => Ok(self),
303 }
304 }
305
306 async fn simulate_via_exec_transaction(
311 &self,
312 simulator: &ForkSimulator<P>,
313 to: Address,
314 value: U256,
315 data: Bytes,
316 operation: Operation,
317 ) -> Result<SimulationResult> {
318 let nonce = self.safe.nonce().await?;
320
321 let safe_tx_gas = U256::from(10_000_000);
323
324 let params = SafeTxParams {
326 to,
327 value,
328 data: data.clone(),
329 operation,
330 safe_tx_gas,
331 base_gas: U256::ZERO,
332 gas_price: U256::ZERO,
333 gas_token: Address::ZERO,
334 refund_receiver: Address::ZERO,
335 nonce,
336 };
337
338 let tx_hash = compute_safe_transaction_hash(
340 self.safe.config.chain_id,
341 self.safe.address,
342 ¶ms,
343 );
344
345 let signature = sign_hash(&self.safe.signer, tx_hash).await?;
347
348 let exec_call = ISafe::execTransactionCall {
350 to: params.to,
351 value: params.value,
352 data: params.data,
353 operation: params.operation.as_u8(),
354 safeTxGas: params.safe_tx_gas,
355 baseGas: params.base_gas,
356 gasPrice: params.gas_price,
357 gasToken: params.gas_token,
358 refundReceiver: params.refund_receiver,
359 signatures: signature,
360 };
361
362 let exec_data = Bytes::from(exec_call.abi_encode());
363
364 simulator
366 .simulate_call(
367 self.safe.signer.address(), self.safe.address, U256::ZERO, exec_data,
371 Operation::Call, )
373 .await
374 }
375
376 pub fn simulation_result(&self) -> Option<&SimulationResult> {
378 self.simulation_result.as_ref()
379 }
380
381 pub async fn execute(self) -> Result<ExecutionResult> {
387 if self.calls.is_empty() {
388 return Err(Error::NoCalls);
389 }
390
391 let (to, value, data, operation) = self.build_call_params()?;
392
393 let nonce = self.safe.nonce().await?;
395
396 let safe_tx_gas = match (&self.simulation_result, self.safe_tx_gas) {
398 (_, Some(gas)) => gas, (Some(sim), None) => {
400 let gas_used = sim.gas_used;
402 U256::from(gas_used + gas_used / 10)
403 }
404 (None, None) => {
405 use alloy::network::TransactionBuilder;
407 let tx_request = <AnyNetwork as alloy::network::Network>::TransactionRequest::default()
408 .with_from(self.safe.address)
409 .with_to(to)
410 .with_value(value)
411 .with_input(data.clone());
412
413 let estimated = self
414 .safe
415 .provider
416 .estimate_gas(tx_request)
417 .await
418 .map_err(|e| Error::Provider(format!("gas estimation failed: {}", e)))?;
419
420 U256::from(estimated + estimated / 10)
422 }
423 };
424
425 let params = SafeTxParams {
427 to,
428 value,
429 data: data.clone(),
430 operation,
431 safe_tx_gas,
432 base_gas: U256::ZERO,
433 gas_price: U256::ZERO,
434 gas_token: Address::ZERO,
435 refund_receiver: Address::ZERO,
436 nonce,
437 };
438
439 let tx_hash = compute_safe_transaction_hash(
441 self.safe.config.chain_id,
442 self.safe.address,
443 ¶ms,
444 );
445
446 let signature = sign_hash(&self.safe.signer, tx_hash).await?;
448
449 let exec_call = ISafe::execTransactionCall {
451 to: params.to,
452 value: params.value,
453 data: params.data,
454 operation: params.operation.as_u8(),
455 safeTxGas: params.safe_tx_gas,
456 baseGas: params.base_gas,
457 gasPrice: params.gas_price,
458 gasToken: params.gas_token,
459 refundReceiver: params.refund_receiver,
460 signatures: signature,
461 };
462
463 let safe_contract = ISafe::new(self.safe.address, &self.safe.provider);
465
466 let builder = safe_contract.execTransaction(
467 exec_call.to,
468 exec_call.value,
469 exec_call.data,
470 exec_call.operation,
471 exec_call.safeTxGas,
472 exec_call.baseGas,
473 exec_call.gasPrice,
474 exec_call.gasToken,
475 exec_call.refundReceiver,
476 exec_call.signatures,
477 );
478
479 let pending_tx = builder
480 .send()
481 .await
482 .map_err(|e| Error::ExecutionFailed {
483 reason: e.to_string(),
484 })?;
485
486 let receipt = pending_tx
487 .get_receipt()
488 .await
489 .map_err(|e| Error::ExecutionFailed {
490 reason: e.to_string(),
491 })?;
492
493 let success = receipt.status();
495
496 Ok(ExecutionResult {
497 tx_hash: receipt.transaction_hash,
498 success,
499 })
500 }
501
502 fn build_call_params(&self) -> Result<(Address, U256, Bytes, Operation)> {
503 if self.calls.len() == 1 {
504 let call = &self.calls[0];
506 Ok((call.to, call.value, call.data.clone(), Operation::Call))
507 } else {
508 let multisend_data = encode_multisend_data(&self.calls);
510
511 let (multisend_address, calldata) = if self.use_call_only {
512 let call = IMultiSendCallOnly::multiSendCall {
513 transactions: multisend_data,
514 };
515 (
516 self.safe.addresses().multi_send_call_only,
517 Bytes::from(call.abi_encode()),
518 )
519 } else {
520 let call = IMultiSend::multiSendCall {
521 transactions: multisend_data,
522 };
523 (
524 self.safe.addresses().multi_send,
525 Bytes::from(call.abi_encode()),
526 )
527 };
528
529 Ok((multisend_address, U256::ZERO, calldata, Operation::DelegateCall))
531 }
532 }
533}
534
535impl<P> CallBuilder for SafeBuilder<'_, P>
536where
537 P: Provider<AnyNetwork> + Clone + Send + Sync + 'static,
538{
539 fn calls_mut(&mut self) -> &mut Vec<Call> {
540 &mut self.calls
541 }
542
543 fn calls(&self) -> &Vec<Call> {
544 &self.calls
545 }
546
547 fn with_gas_limit(self, gas_limit: u64) -> Self {
548 SafeBuilder::with_gas_limit(self, gas_limit)
549 }
550
551 async fn simulate(self) -> Result<Self> {
552 SafeBuilder::simulate(self).await
553 }
554
555 fn simulation_result(&self) -> Option<&SimulationResult> {
556 self.simulation_result.as_ref()
557 }
558
559 fn simulation_success(self) -> Result<Self> {
560 SafeBuilder::simulation_success(self)
561 }
562}
563
564impl<P> crate::account::Account for Safe<P>
565where
566 P: Provider<AnyNetwork> + Clone + Send + Sync + 'static,
567{
568 type Provider = P;
569 type Builder<'a> = SafeBuilder<'a, P> where Self: 'a;
570
571 fn address(&self) -> Address {
572 self.address
573 }
574
575 fn signer_address(&self) -> Address {
576 self.signer.address()
577 }
578
579 fn config(&self) -> &ChainConfig {
580 &self.config
581 }
582
583 fn provider(&self) -> &P {
584 &self.provider
585 }
586
587 fn debug_output_dir(&self) -> Option<&Path> {
588 self.debug_output_dir.as_deref()
589 }
590
591 async fn nonce(&self) -> Result<U256> {
592 let safe = ISafe::new(self.address, &self.provider);
593 let nonce = safe
594 .nonce()
595 .call()
596 .await
597 .map_err(|e| Error::Fetch {
598 what: "nonce",
599 reason: e.to_string(),
600 })?;
601 Ok(nonce)
602 }
603
604 fn batch(&self) -> SafeBuilder<'_, P> {
605 SafeBuilder::new(self)
606 }
607
608 async fn execute_single(
609 &self,
610 to: Address,
611 value: U256,
612 data: Bytes,
613 operation: Operation,
614 ) -> Result<ExecutionResult> {
615 self.batch()
616 .add_raw(to, value, data)
617 .with_operation(operation)
618 .simulate()
619 .await?
620 .simulation_success()?
621 .execute()
622 .await
623 }
624}
625
626#[cfg(test)]
627mod tests {
628 #[allow(unused_imports)]
629 use super::*;
630 use alloy::primitives::address;
631
632 #[test]
633 fn test_call_params_single() {
634 let _addr = address!("0x1234567890123456789012345678901234567890");
637 }
638
639 #[test]
640 fn test_safe_singleton_slot_is_zero() {
641 assert_eq!(SAFE_SINGLETON_SLOT, U256::ZERO);
642 }
643}