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