Skip to main content

builder_relayer_client_rust/encode/
safe.rs

1use crate::types::{OperationType, SafeTransaction};
2use alloy::primitives::{Address, U256};
3use alloy::sol;
4use alloy::sol_types::SolCall;
5
6// pack single transaction like Solidity struct used by Gnosis Safe MultiSend
7fn pack_tx(tx: &SafeTransaction) -> Vec<u8> {
8    // bytes: operation (1) + to (20) + value (32) + data length (32) + data
9    let mut out = Vec::new();
10    out.push(tx.operation as u8);
11
12    // address
13    let addr: Address = tx.to.parse().unwrap_or(Address::ZERO);
14    out.extend_from_slice(addr.as_slice());
15
16    // value (uint256)
17    let value = U256::from_str_radix(&tx.value, 10).unwrap_or(U256::ZERO);
18    out.extend_from_slice(&value.to_be_bytes::<32>());
19
20    // data length (uint256)
21    let data_bytes = hex::decode(tx.data.trim_start_matches("0x")).unwrap_or_default();
22    let len = U256::from(data_bytes.len());
23    out.extend_from_slice(&len.to_be_bytes::<32>());
24
25    // data
26    out.extend_from_slice(&data_bytes);
27    out
28}
29
30pub fn create_safe_multisend_transaction(
31    txns: &[SafeTransaction],
32    safe_multisend_address: &str,
33) -> SafeTransaction {
34    if txns.len() == 1 {
35        return txns[0].clone();
36    }
37    let mut packed: Vec<u8> = Vec::new();
38    for t in txns {
39        packed.extend_from_slice(&pack_tx(t));
40    }
41
42    // encode function selector + encoded bytes via abi
43    // multiSend(bytes)
44    sol! {
45        function multiSend(bytes memory transactions);
46    }
47
48    let call = multiSendCall {
49        transactions: packed.into(),
50    };
51    let calldata = call.abi_encode();
52
53    SafeTransaction {
54        to: safe_multisend_address.to_string(),
55        value: "0".to_string(),
56        data: format!("0x{}", hex::encode(calldata)),
57        operation: OperationType::DelegateCall,
58    }
59}