builder_relayer_client_rust/builder/
create.rs1use crate::builder::derive::derive_safe;
2use crate::errors::Result;
3use crate::types::{
4 SafeCreateTransactionArgs, SignatureParams, TransactionRequest, TransactionType,
5};
6use crate::utils::split_and_pack_sig;
7use ethers::abi::{encode, Token};
8use ethers::types::{Address, U256};
9use sha3::{Digest, Keccak256};
10
11const SAFE_CREATE_TYPE_STR: &str = "SafeCreate(address owner,address paymentToken,uint256 payment,address paymentReceiver,uint256 nonce)";
14const DOMAIN_TYPE_STR: &str = "EIP712Domain(uint256 chainId,address verifyingContract)";
15
16fn keccak_bytes(data: &[u8]) -> [u8; 32] {
17 let mut h = Keccak256::new();
18 h.update(data);
19 let out = h.finalize();
20 let mut arr = [0u8; 32];
21 arr.copy_from_slice(&out);
22 arr
23}
24
25fn safe_create_type_hash() -> [u8; 32] {
26 keccak_bytes(SAFE_CREATE_TYPE_STR.as_bytes())
27}
28fn domain_type_hash() -> [u8; 32] {
29 keccak_bytes(DOMAIN_TYPE_STR.as_bytes())
30}
31
32fn eip712_domain_separator(chain_id: U256, verifying_contract: Address) -> [u8; 32] {
33 let encoded = encode(&[
34 Token::FixedBytes(domain_type_hash().to_vec()),
35 Token::Uint(chain_id),
36 Token::Address(verifying_contract),
37 ]);
38 keccak_bytes(&encoded)
39}
40
41fn safe_create_struct_hash(
42 owner: Address,
43 payment_token: Address,
44 payment: U256,
45 payment_receiver: Address,
46 nonce: U256,
47) -> [u8; 32] {
48 let encoded = encode(&[
49 Token::FixedBytes(safe_create_type_hash().to_vec()),
50 Token::Address(owner),
51 Token::Address(payment_token),
52 Token::Uint(payment),
53 Token::Address(payment_receiver),
54 Token::Uint(nonce),
55 ]);
56 keccak_bytes(&encoded)
57}
58
59pub trait TypedDataSigner: Send + Sync {
60 fn sign_typed_create_proxy(
61 &self,
62 safe_factory: &str,
63 chain_id: u64,
64 payment_token: &str,
65 payment: &str,
66 payment_receiver: &str,
67 ) -> Result<String>;
68}
69
70pub trait AbstractSignerForCreate: Send + Sync {
71 fn get_address(&self) -> Result<String>;
72 fn sign_eip712_digest(&self, digest_hex: &str) -> Result<String>;
73}
74
75pub async fn build_safe_create_transaction_request(
76 signer: &dyn AbstractSignerForCreate,
77 safe_factory: &str,
78 args: SafeCreateTransactionArgs,
79) -> Result<TransactionRequest> {
80 let owner: Address = args
81 .from
82 .parse()
83 .map_err(|_| crate::errors::RelayClientError::SignerUnavailable)?;
84 let factory: Address = safe_factory
85 .parse()
86 .map_err(|_| crate::errors::RelayClientError::SignerUnavailable)?;
87 let payment_token: Address = args
88 .payment_token
89 .parse()
90 .map_err(|_| crate::errors::RelayClientError::SignerUnavailable)?;
91 let payment_receiver: Address = args
92 .payment_receiver
93 .parse()
94 .map_err(|_| crate::errors::RelayClientError::SignerUnavailable)?;
95 let payment = U256::from_dec_str(&args.payment).unwrap_or_default();
96
97 let nonce = U256::zero();
100
101 let struct_hash =
102 safe_create_struct_hash(owner, payment_token, payment, payment_receiver, nonce);
103 let domain_separator = eip712_domain_separator(U256::from(args.chain_id), factory);
104
105 let mut prefix = vec![0x19, 0x01];
106 prefix.extend_from_slice(&domain_separator);
107 prefix.extend_from_slice(&struct_hash);
108 let digest = keccak_bytes(&prefix);
109
110 let sig = signer.sign_eip712_digest(&format!("0x{}", hex::encode(digest)))?;
111 let packed_sig = split_and_pack_sig(&sig);
112
113 let sig_params = SignatureParams {
114 payment_token: Some(args.payment_token.clone()),
115 payment: Some(args.payment.clone()),
116 payment_receiver: Some(args.payment_receiver.clone()),
117 ..Default::default()
118 };
119
120 let safe_address = derive_safe(&args.from, safe_factory);
121
122 let req = TransactionRequest {
123 from: args.from.clone(),
124 to: safe_factory.to_string(),
125 proxy_wallet: Some(safe_address),
126 data: "0x".to_string(),
127 nonce: None,
128 signature: packed_sig,
129 signature_params: sig_params,
130 r#type: TransactionType::SafeCreate,
131 metadata: None,
132 };
133 Ok(req)
134}