safe_rs/types/
call.rs

1//! Call types for Safe transactions
2
3use alloy::primitives::{Address, Bytes, U256};
4use alloy::sol_types::SolCall;
5
6use super::Operation;
7
8/// Trait for types that can be converted to a Safe call
9pub trait SafeCall {
10    /// Returns the target address
11    fn to(&self) -> Address;
12
13    /// Returns the value to send (in wei)
14    fn value(&self) -> U256;
15
16    /// Returns the calldata
17    fn data(&self) -> Bytes;
18
19    /// Returns the operation type (Call or DelegateCall)
20    fn operation(&self) -> Operation;
21}
22
23/// A raw call with explicit to, value, data, and operation
24#[derive(Debug, Clone)]
25pub struct Call {
26    /// Target address
27    pub to: Address,
28    /// Value to send
29    pub value: U256,
30    /// Calldata
31    pub data: Bytes,
32    /// Operation type
33    pub operation: Operation,
34}
35
36impl Call {
37    /// Creates a new Call with the given parameters
38    pub fn new(to: Address, value: U256, data: impl Into<Bytes>) -> Self {
39        Self {
40            to,
41            value,
42            data: data.into(),
43            operation: Operation::Call,
44        }
45    }
46
47    /// Creates a new Call with zero value
48    pub fn call(to: Address, data: impl Into<Bytes>) -> Self {
49        Self::new(to, U256::ZERO, data)
50    }
51
52    /// Creates a new delegate call
53    pub fn delegate_call(to: Address, data: impl Into<Bytes>) -> Self {
54        Self {
55            to,
56            value: U256::ZERO,
57            data: data.into(),
58            operation: Operation::DelegateCall,
59        }
60    }
61
62    /// Sets the operation type
63    pub fn with_operation(mut self, operation: Operation) -> Self {
64        self.operation = operation;
65        self
66    }
67
68    /// Sets the value
69    pub fn with_value(mut self, value: U256) -> Self {
70        self.value = value;
71        self
72    }
73}
74
75impl SafeCall for Call {
76    fn to(&self) -> Address {
77        self.to
78    }
79
80    fn value(&self) -> U256 {
81        self.value
82    }
83
84    fn data(&self) -> Bytes {
85        self.data.clone()
86    }
87
88    fn operation(&self) -> Operation {
89        self.operation
90    }
91}
92
93/// A typed call wrapping a sol! macro generated call type
94#[derive(Debug, Clone)]
95pub struct TypedCall<C: SolCall> {
96    /// Target address
97    pub to: Address,
98    /// Value to send
99    pub value: U256,
100    /// The typed call data
101    pub call: C,
102    /// Operation type
103    pub operation: Operation,
104}
105
106impl<C: SolCall> TypedCall<C> {
107    /// Creates a new TypedCall
108    pub fn new(to: Address, call: C) -> Self {
109        Self {
110            to,
111            value: U256::ZERO,
112            call,
113            operation: Operation::Call,
114        }
115    }
116
117    /// Creates a new TypedCall with value
118    pub fn with_value(mut self, value: U256) -> Self {
119        self.value = value;
120        self
121    }
122
123    /// Sets the operation type
124    pub fn with_operation(mut self, operation: Operation) -> Self {
125        self.operation = operation;
126        self
127    }
128}
129
130impl<C: SolCall + Clone> SafeCall for TypedCall<C> {
131    fn to(&self) -> Address {
132        self.to
133    }
134
135    fn value(&self) -> U256 {
136        self.value
137    }
138
139    fn data(&self) -> Bytes {
140        self.call.abi_encode().into()
141    }
142
143    fn operation(&self) -> Operation {
144        self.operation
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    use alloy::primitives::address;
152
153    #[test]
154    fn test_call_new() {
155        let to = address!("0x1234567890123456789012345678901234567890");
156        let value = U256::from(1000);
157        let data = Bytes::from(vec![0x01, 0x02, 0x03]);
158
159        let call = Call::new(to, value, data.clone());
160
161        assert_eq!(call.to(), to);
162        assert_eq!(call.value(), value);
163        assert_eq!(call.data(), data);
164        assert_eq!(call.operation(), Operation::Call);
165    }
166
167    #[test]
168    fn test_call_delegate() {
169        let to = address!("0x1234567890123456789012345678901234567890");
170        let data = Bytes::from(vec![0x01, 0x02, 0x03]);
171
172        let call = Call::delegate_call(to, data.clone());
173
174        assert_eq!(call.to(), to);
175        assert_eq!(call.value(), U256::ZERO);
176        assert_eq!(call.data(), data);
177        assert_eq!(call.operation(), Operation::DelegateCall);
178    }
179}