builder_relayer_client_rust/builder/
proxy.rs1use crate::builder::derive::derive_proxy;
2use crate::encode::proxy::encode_proxy_transaction_data;
3use crate::errors::{RelayClientError, Result};
4use crate::signer::AbstractSigner;
5use crate::types::{
6 ProxyTransaction, ProxyTransactionArgs, SignatureParams, TransactionRequest, TransactionType,
7};
8use alloy::primitives::{Address, B256, U256, keccak256};
9use std::str::FromStr;
10
11const DEFAULT_GAS_LIMIT: &str = "10000000";
12const RELAY_HUB_PREFIX: &[u8] = b"rlx:";
13
14#[derive(Clone, Debug)]
15pub struct ProxyContractConfig {
16 pub relay_hub: String,
17 pub proxy_factory: String,
18}
19
20pub fn is_proxy_contract_config_valid(config: &ProxyContractConfig) -> bool {
21 !config.proxy_factory.is_empty() && !config.relay_hub.is_empty()
22}
23
24fn as_be_bytes_32(value: &str) -> U256 {
25 U256::from_str_radix(value, 10).unwrap_or(U256::ZERO)
26}
27
28fn create_struct_hash(
29 from: &str,
30 to: &str,
31 data_hex: &str,
32 tx_fee: &str,
33 gas_price: &str,
34 gas_limit: &str,
35 nonce: &str,
36 relay_hub: &str,
37 relay: &str,
38) -> Result<B256> {
39 let from_addr = Address::from_str(from).map_err(|_| RelayClientError::InvalidAddress)?;
40 let to_addr = Address::from_str(to).map_err(|_| RelayClientError::InvalidAddress)?;
41 let relay_hub_addr =
42 Address::from_str(relay_hub).map_err(|_| RelayClientError::InvalidAddress)?;
43 let relay_addr = Address::from_str(relay).map_err(|_| RelayClientError::InvalidAddress)?;
44
45 let mut data_bytes = data_hex.trim_start_matches("0x").as_bytes().to_vec();
46 if let Ok(decoded) = hex::decode(data_hex.trim_start_matches("0x")) {
47 data_bytes = decoded;
48 }
49
50 let tx_fee_bytes = as_be_bytes_32(tx_fee).to_be_bytes::<32>();
51 let gas_price_bytes = as_be_bytes_32(gas_price).to_be_bytes::<32>();
52 let gas_limit_bytes = as_be_bytes_32(gas_limit).to_be_bytes::<32>();
53 let nonce_bytes = as_be_bytes_32(nonce).to_be_bytes::<32>();
54
55 let mut payload: Vec<u8> = Vec::new();
56 payload.extend_from_slice(RELAY_HUB_PREFIX);
57 payload.extend_from_slice(from_addr.as_slice());
58 payload.extend_from_slice(to_addr.as_slice());
59 payload.extend_from_slice(&data_bytes);
60 payload.extend_from_slice(&tx_fee_bytes);
61 payload.extend_from_slice(&gas_price_bytes);
62 payload.extend_from_slice(&gas_limit_bytes);
63 payload.extend_from_slice(&nonce_bytes);
64 payload.extend_from_slice(relay_hub_addr.as_slice());
65 payload.extend_from_slice(relay_addr.as_slice());
66
67 Ok(keccak256(payload))
68}
69
70async fn create_proxy_signature(signer: &dyn AbstractSigner, struct_hash: B256) -> Result<String> {
71 let mut msg = Vec::with_capacity(2 + 32 + 32);
73 msg.extend_from_slice(b"\x19Ethereum Signed Message:\n32");
74 msg.extend_from_slice(struct_hash.as_slice());
75 let digest = keccak256(&msg);
76
77 let sig = signer.sign_hash(digest).await?;
78 Ok(format!("0x{}", hex::encode(sig.as_bytes())))
79}
80
81pub async fn build_proxy_transaction_request(
82 signer: &dyn AbstractSigner,
83 args: &ProxyTransactionArgs,
84 proxy_contract_config: &ProxyContractConfig,
85 metadata: Option<String>,
86 txns: &[ProxyTransaction],
87) -> Result<TransactionRequest> {
88 if !is_proxy_contract_config_valid(proxy_contract_config) {
89 return Err(RelayClientError::InvalidNetwork);
90 }
91
92 let proxy_factory = &proxy_contract_config.proxy_factory;
93 let from_addr = Address::from_str(&args.from).map_err(|_| RelayClientError::InvalidAddress)?;
94 let factory_addr =
95 Address::from_str(proxy_factory).map_err(|_| RelayClientError::InvalidAddress)?;
96 let proxy_wallet = derive_proxy(factory_addr, from_addr)?.to_string();
97
98 let relayer_fee = "0".to_string();
99 let gas_limit = args
100 .gas_limit
101 .clone()
102 .filter(|g| !g.is_empty() && g != "0")
103 .unwrap_or_else(|| DEFAULT_GAS_LIMIT.to_string());
104
105 let struct_hash = create_struct_hash(
106 &args.from,
107 proxy_factory,
108 &args.data,
109 &relayer_fee,
110 &args.gas_price,
111 &gas_limit,
112 &args.nonce,
113 &proxy_contract_config.relay_hub,
114 &args.relay,
115 )?;
116
117 let signature = create_proxy_signature(signer, struct_hash).await?;
118
119 let sig_params = SignatureParams {
120 gas_price: Some(args.gas_price.clone()),
121 relayer_fee: Some(relayer_fee.clone()),
122 gas_limit: Some(gas_limit.clone()),
123 relay_hub: Some(proxy_contract_config.relay_hub.clone()),
124 relay: Some(args.relay.clone()),
125 ..SignatureParams::default()
126 };
127
128 let packed_metadata = metadata.unwrap_or_default();
129 let data = encode_proxy_transaction_data(txns);
130
131 Ok(TransactionRequest {
132 r#type: TransactionType::Proxy,
133 from: args.from.clone(),
134 to: proxy_factory.clone(),
135 proxy_wallet: Some(proxy_wallet),
136 data,
137 nonce: Some(args.nonce.clone()),
138 signature,
139 signature_params: sig_params,
140 metadata: Some(packed_metadata),
141 })
142}