polytone_evm/
evm.rs

1use crate::erc20::Erc20Token;
2use alloy::primitives::{Address, AddressError, U256};
3use alloy_sol_types::SolValue;
4use cosmwasm_schema::cw_serde;
5use cosmwasm_std::{HexBinary, Uint128};
6
7/// Marks either `String` or valid EVM `Address`.
8///
9/// String is used in unverified types, such as messages and query responses.
10/// Addr is used in verified types, which are to be stored in blockchain state.
11///
12/// This trait is intended to be used as a generic in type definitions.
13pub trait EvmAddressLike {}
14
15impl EvmAddressLike for String {}
16
17impl EvmAddressLike for HexBinary {}
18
19impl EvmAddressLike for Address {}
20
21/// A message to be sent to the EVM
22#[cw_serde]
23#[non_exhaustive]
24pub enum EvmMsg<T: EvmAddressLike> {
25    /// Call a contract with the given data
26    Call {
27        /// The address of the contract to call, must pass checksum validation
28        to: T,
29        /// The calldata to send to the contract
30        data: HexBinary,
31        /// Native ETH used in the tx
32        #[serde(skip_serializing_if = "Option::is_none")]
33        value: Option<Uint128>,
34        /// Don't revert entire batch when this transaction fails
35        #[serde(skip_serializing_if = "Option::is_none")]
36        allow_failure: Option<bool>,
37    },
38    /// Delegate a call to an external contract. This is dangerous and should be used with caution.
39    DelegateCall {
40        /// The address of the contract to call, must pass checksum validation
41        to: T,
42        /// The calldata to send to the contract
43        data: HexBinary,
44        /// Native ETH used in the tx
45        #[serde(skip_serializing_if = "Option::is_none")]
46        value: Option<Uint128>,
47        /// Don't revert entire batch when this transaction fails
48        #[serde(skip_serializing_if = "Option::is_none")]
49        allow_failure: Option<bool>,
50    },
51}
52
53impl<T> EvmMsg<T>
54where
55    T: EvmAddressLike,
56{
57    pub fn call(to: T, data: impl Into<HexBinary>) -> Self {
58        EvmMsg::Call {
59            to,
60            data: data.into(),
61            value: None,
62            allow_failure: None,
63        }
64    }
65
66    pub fn call_with_value(to: T, data: impl Into<HexBinary>, value: Uint128) -> Self {
67        EvmMsg::Call {
68            to,
69            data: data.into(),
70            value: Some(value),
71            allow_failure: None,
72        }
73    }
74
75    pub fn delegate_call(to: T, data: impl Into<HexBinary>) -> Self {
76        EvmMsg::DelegateCall {
77            to,
78            data: data.into(),
79            value: None,
80            allow_failure: None,
81        }
82    }
83}
84
85impl EvmMsg<String> {
86    /// Check the validity of the addresses
87    pub fn check(self) -> Result<EvmMsg<Address>, AddressError> {
88        match self {
89            EvmMsg::Call {
90                to,
91                data,
92                value,
93                allow_failure,
94            } => {
95                let to: Address = Address::parse_checksummed(to, None)?;
96                Ok(EvmMsg::Call {
97                    to,
98                    data,
99                    value,
100                    allow_failure,
101                })
102            }
103            EvmMsg::DelegateCall {
104                to,
105                data,
106                value,
107                allow_failure,
108            } => {
109                let to: Address = Address::parse_checksummed(to, None)?;
110                Ok(EvmMsg::DelegateCall {
111                    to,
112                    data,
113                    value,
114                    allow_failure,
115                })
116            }
117        }
118    }
119}
120
121impl EvmMsg<Address> {
122    pub fn encode(self) -> Vec<u8> {
123        match self {
124            EvmMsg::Call {
125                to,
126                data,
127                allow_failure,
128                value,
129            } => {
130                let data = data.to_vec();
131                let call = abi_types::CallMessage {
132                    to,
133                    data: data.to_vec().into(),
134                    allowFailure: allow_failure.unwrap_or(false),
135                    value: value.map(|v| U256::from(v.u128())).unwrap_or_default(),
136                }
137                .abi_encode();
138                abi_types::EvmMsg {
139                    msgType: abi_types::EvmMsgType::Call,
140                    message: call.into(),
141                }
142                .abi_encode()
143            }
144            EvmMsg::DelegateCall {
145                to,
146                data,
147                allow_failure,
148                value,
149            } => {
150                let data = data.to_vec();
151                let call = abi_types::CallMessage {
152                    to,
153                    data: data.to_vec().into(),
154                    allowFailure: allow_failure.unwrap_or(false),
155                    value: value.map(|v| U256::from(v.u128())).unwrap_or_default(),
156                }
157                .abi_encode();
158                abi_types::EvmMsg {
159                    msgType: abi_types::EvmMsgType::DelegateCall,
160                    message: call.into(),
161                }
162                .abi_encode()
163            }
164        }
165    }
166
167    #[allow(dead_code)]
168    fn unchecked(self) -> EvmMsg<String> {
169        match self {
170            EvmMsg::Call {
171                to,
172                data,
173                allow_failure,
174                value,
175            } => EvmMsg::Call {
176                to: to.to_string(),
177                data,
178                allow_failure,
179                value,
180            },
181            EvmMsg::DelegateCall {
182                to,
183                data,
184                allow_failure,
185                value,
186            } => EvmMsg::DelegateCall {
187                to: to.to_string(),
188                data,
189                allow_failure,
190                value,
191            },
192        }
193    }
194}
195
196pub(crate) mod abi_types {
197    use alloy_sol_types::sol;
198
199    // Copied directly from solidity/src/Requests.sol
200    sol! {
201    // Reflect the CW Packet
202    struct Packet {
203        string sender;
204        Msg msg;
205    }
206
207    // Message called on the voice
208    struct Msg {
209        MsgType msgType;
210        // Requests on the voice (Exec / Query)
211        bytes[] data;
212    }
213
214    // Type of voice message
215    enum MsgType {
216        Execute
217    //    Query
218    }
219
220    // Message called on the proxy contract
221    // Reflect EvmMsg<Address>
222    struct EvmMsg {
223        EvmMsgType msgType;
224        bytes message;
225    }
226
227    // Type of message called on proxy contract
228    enum EvmMsgType {
229        Call,
230        DelegateCall,
231    }
232
233    // Data to execute by proxy
234    struct CallMessage {
235        address to;
236        bool allowFailure;
237        uint256 value;
238        bytes data;
239    }
240
241
242    struct ExecuteResult {
243        bool success;
244        bytes data;
245    }
246
247    struct ExecuteResponsePacket {
248        address executedBy;
249        ExecuteResult[] result;
250    }
251
252
253    struct Token {
254        address denom;
255        uint128 amount;
256    }
257
258    }
259}
260
261impl From<abi_types::Token> for Erc20Token<String> {
262    fn from(token: abi_types::Token) -> Self {
263        Erc20Token {
264            address: token.denom.to_string(),
265            amount: token.amount.into(),
266        }
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use super::*;
273
274    mod call_message {
275        use crate::ibc::{Msg, Packet};
276
277        use super::*;
278
279        #[test]
280        fn unchecked_encoding() {
281            let msg = abi_types::CallMessage {
282                to: "0x785B548D3d7064F77A26e479AC7847DBCE0c1B46"
283                    .parse()
284                    .unwrap(),
285                data: HexBinary::from_hex("b49004e9").unwrap().to_vec().into(),
286                allowFailure: false,
287                value: U256::from(0),
288            };
289
290            let encoded = msg.abi_encode();
291            let actual = HexBinary::from(encoded);
292
293            let expected = HexBinary::from_hex("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000785b548d3d7064f77a26e479ac7847dbce0c1b460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004b49004e900000000000000000000000000000000000000000000000000000000").unwrap();
294
295            assert_eq!(actual, expected);
296        }
297
298        #[test]
299        fn checked_encoding() {
300            let msg = EvmMsg::call(
301                "0x785B548D3d7064F77A26e479AC7847DBCE0c1B46".to_string(),
302                HexBinary::from_hex("b49004e9").unwrap(),
303            )
304            .check()
305            .unwrap();
306
307            let encoded = msg.encode();
308            let actual = HexBinary::from(encoded);
309
310            let expected = HexBinary::from_hex("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000785b548d3d7064f77a26e479ac7847dbce0c1b460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004b49004e900000000000000000000000000000000000000000000000000000000").unwrap();
311
312            assert_eq!(actual, expected);
313        }
314
315        const TEST_SENDER: &str =
316            "union1tw2y3uk7fwcjeh208gdwaqd3utkuzmnp2fyvqve00jg8vtyhuhfqsender";
317
318        #[test]
319        fn packet_encoding() {
320            let evm_msg = EvmMsg::call(
321                "0x785B548D3d7064F77A26e479AC7847DBCE0c1B46".to_string(),
322                HexBinary::from_hex("b49004e9").unwrap(),
323            );
324
325            let msg = Msg::Execute {
326                msgs: vec![evm_msg],
327            };
328            let request: Packet = Packet {
329                msg,
330                sender: TEST_SENDER.to_string(),
331            };
332            let encoded = request.encode().unwrap();
333            let actual = HexBinary::from(encoded);
334
335            let expected = HexBinary::from_hex("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040756e696f6e317477327933756b376677636a65683230386764776171643375746b757a6d6e703266797671766530306a6738767479687568667173656e6465720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000785b548d3d7064f77a26e479ac7847dbce0c1b460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004b49004e900000000000000000000000000000000000000000000000000000000").unwrap();
336
337            assert_eq!(actual, expected);
338        }
339    }
340
341    #[test]
342    fn test_address() {
343        EvmMsg::call(
344            "0x785B548D3d7064F77A26e479AC7847DBCE0c1B46".to_string(),
345            HexBinary::from_hex("b49004e9").unwrap(),
346        );
347    }
348}