zksync_web3_rs/
zks_utils.rs

1use ethers::{
2    abi::{
3        encode,
4        token::{LenientTokenizer, StrictTokenizer, Tokenizer},
5        Constructor, Function, Param, ParamType, Token,
6    },
7    types::{Address, H160, U256},
8};
9use ethers_contract::AbiError;
10use std::str::FromStr;
11
12/* Misc */
13
14pub const ETH_CHAIN_ID: u16 = 0x9;
15pub const ERA_CHAIN_ID: u16 = 0x10E;
16pub const ERA_MAINNET_CHAIN_ID: u16 = 324;
17
18pub const EIP712_TX_TYPE: u8 = 0x71;
19// The large L2 gas per pubdata to sign. This gas is enough to ensure that
20// any reasonable limit will be accepted. Note, that the operator is NOT required to
21// use the honest value of gas per pubdata and it can use any value up to the one signed by the user.
22// In the future releases, we will provide a way to estimate the current gasPerPubdata.
23pub const DEFAULT_GAS_PER_PUBDATA_LIMIT: u64 = 50000;
24pub const MAX_PRIORITY_FEE_PER_GAS: u64 = 1063439364;
25pub const MAX_FEE_PER_GAS: u64 = 1063439378;
26pub const DEFAULT_GAS: u64 = 91435;
27/// This the number of pubdata such that it should be always possible to publish
28/// from a single transaction. Note, that these pubdata bytes include only bytes that are
29/// to be published inside the body of transaction (i.e. excluding of factory deps).
30pub const GUARANTEED_PUBDATA_PER_L1_BATCH: u64 = 4000;
31pub const MAX_L2_TX_GAS_LIMIT: u64 = 80000000;
32// The users should always be able to provide `MAX_GAS_PER_PUBDATA_BYTE` gas per pubdata in their
33// transactions so that they are able to send at least GUARANTEED_PUBDATA_PER_L1_BATCH bytes per
34// transaction.
35pub const MAX_GAS_PER_PUBDATA_BYTE: u64 = MAX_L2_TX_GAS_LIMIT / GUARANTEED_PUBDATA_PER_L1_BATCH;
36
37pub const RECOMMENDED_DEPOSIT_L1_GAS_LIMIT: u64 = 10000000;
38pub const RECOMMENDED_DEPOSIT_L2_GAS_LIMIT: u64 = 10000000;
39pub const DEPOSIT_GAS_PER_PUBDATA_LIMIT: u64 = 800;
40pub const DEFAULT_ERC20_DEPOSIT_GAS_LIMIT: u64 = 300000_u64;
41
42/* Contracts */
43
44pub const CHAIN_STATE_KEEPER_BOOTLOADER_HASH: &str =
45    "0x0100038581be3d0e201b3cc45d151ef5cc59eb3a0f146ad44f0f72abf00b594c";
46pub const CHAIN_STATE_KEEPER_DEFAULT_AA_HASH: &str =
47    "0x0100038dc66b69be75ec31653c64cb931678299b9b659472772b2550b703f41c";
48
49pub const CONTRACT_DEPLOYER_ADDR: &str = "0x0000000000000000000000000000000000008006";
50pub const CONTRACTS_DIAMOND_INIT_ADDR: &str = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9";
51pub const CONTRACTS_DIAMOND_UPGRADE_INIT_ADDR: &str = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9";
52pub const CONTRACTS_MAILBOX_FACET_ADDR: &str = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9";
53pub const CONTRACTS_DIAMOND_CUT_FACET_ADDR: &str = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9";
54pub const CONTRACTS_EXECUTOR_FACET_ADDR: &str = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9";
55pub const CONTRACTS_GOVERNANCE_FACET_ADDR: &str = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9";
56pub const CONTRACTS_GETTERS_FACET_ADDR: &str = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9";
57pub const CONTRACTS_VERIFIER_ADDR: &str = "0xDAbb67b676F5b01FcC8997Cc8439846D0d8078ca";
58pub const CONTRACTS_DIAMOND_PROXY_ADDR: &str = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF";
59pub const CONTRACTS_L1_ERC20_BRIDGE_PROXY_ADDR: &str = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF";
60pub const CONTRACTS_L1_ERC20_BRIDGE_IMPL_ADDR: &str = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF";
61pub const CONTRACTS_L2_ERC20_BRIDGE_ADDR: &str = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF";
62pub const CONTRACTS_L2_TESTNET_PAYMASTER_ADDR: &str = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF";
63pub const CONTRACTS_L1_ALLOW_LIST_ADDR: &str = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF";
64pub const CONTRACTS_CREATE2_FACTORY_ADDR: &str = "0xce0042B868300000d44A59004Da54A005ffdcf9f";
65pub const CONTRACTS_VALIDATOR_TIMELOCK_ADDR: &str = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF";
66pub const CONTRACTS_L1_WETH_BRIDGE_IMPL_ADDR: &str = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9";
67pub const CONTRACTS_L1_WETH_BRIDGE_PROXY_ADDR: &str = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9";
68pub const CONTRACTS_L1_WETH_TOKEN_ADDR: &str = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9";
69pub const CONTRACTS_L2_ETH_TOKEN_ADDR: &str = "0x000000000000000000000000000000000000800a";
70pub const CONTRACTS_L1_MESSENGER_ADDR: &str = "0x0000000000000000000000000000000000008008";
71
72pub const ETHER_L1_ADDRESS: Address = H160([
73    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74    0x00, 0x00, 0x00, 0x00,
75]);
76
77/* Precompiles */
78
79pub const ECRECOVER_PRECOMPILE_ADDRESS: Address = H160([
80    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
81    0x00, 0x00, 0x00, 0x01,
82]);
83
84pub const SHA256_PRECOMPILE_ADDRESS: Address = H160([
85    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
86    0x00, 0x00, 0x00, 0x02,
87]);
88
89pub const RIPEMD_160_PRECOMPILE_ADDRESS: Address = H160([
90    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91    0x00, 0x00, 0x00, 0x03,
92]);
93
94pub const IDENTITY_PRECOMPILE_ADDRESS: Address = H160([
95    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96    0x00, 0x00, 0x00, 0x04,
97]);
98
99pub const MODEXP_PRECOMPILE_ADDRESS: Address = H160([
100    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101    0x00, 0x00, 0x00, 0x05,
102]);
103
104pub const ECADD_PRECOMPILE_ADDRESS: Address = H160([
105    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106    0x00, 0x00, 0x00, 0x06,
107]);
108
109pub const ECMUL_PRECOMPILE_ADDRESS: Address = H160([
110    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111    0x00, 0x00, 0x00, 0x07,
112]);
113
114pub const ECPAIRING_PRECOMPILE_ADDRESS: Address = H160([
115    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116    0x00, 0x00, 0x00, 0x08,
117]);
118
119pub const BLAKE2F_PRECOMPILE_ADDRESS: Address = H160([
120    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121    0x00, 0x00, 0x00, 0x09,
122]);
123
124pub fn is_precompile(address: Address) -> bool {
125    address == ECRECOVER_PRECOMPILE_ADDRESS
126        || address == SHA256_PRECOMPILE_ADDRESS
127        || address == RIPEMD_160_PRECOMPILE_ADDRESS
128        || address == IDENTITY_PRECOMPILE_ADDRESS
129        || address == MODEXP_PRECOMPILE_ADDRESS
130        || address == ECADD_PRECOMPILE_ADDRESS
131        || address == ECMUL_PRECOMPILE_ADDRESS
132        || address == ECPAIRING_PRECOMPILE_ADDRESS
133        || address == BLAKE2F_PRECOMPILE_ADDRESS
134}
135
136/// Given a function and a vector of string arguments, it proceeds to convert the args to ethabi
137/// Tokens and then ABI encode them.
138/// > This function was taken from foundry.
139pub fn encode_args(func: &Function, args: &[impl AsRef<str>]) -> Result<Vec<u8>, AbiError> {
140    let params = func
141        .inputs
142        .iter()
143        .zip(args)
144        .map(|(input, arg)| (&input.kind, arg.as_ref()))
145        .collect::<Vec<_>>();
146    let tokens = parse_tokens(params, true)?;
147    Ok(encode(&tokens))
148}
149
150/// Given a constructor and a vector of string arguments, it proceeds to convert the args to ethabi
151/// Tokens and then ABI encode them.
152pub fn encode_constructor_args(
153    constructor: &Constructor,
154    args: &[impl AsRef<str>],
155) -> Result<Vec<u8>, AbiError> {
156    let params = constructor
157        .inputs
158        .iter()
159        .zip(args)
160        .map(|(input, arg)| (&input.kind, arg.as_ref()))
161        .collect::<Vec<_>>();
162    let tokens = parse_tokens(params, true)?;
163    Ok(encode(&tokens))
164}
165
166/// Parses string input as Token against the expected ParamType
167/// > This function was taken from foundry.
168pub fn parse_tokens<'a, I: IntoIterator<Item = (&'a ParamType, &'a str)>>(
169    params: I,
170    lenient: bool,
171) -> Result<Vec<Token>, AbiError> {
172    let mut tokens = Vec::new();
173
174    for (param, value) in params.into_iter() {
175        let mut token = if lenient {
176            LenientTokenizer::tokenize(param, value)
177        } else {
178            StrictTokenizer::tokenize(param, value)
179        };
180        if token.is_err() && value.starts_with("0x") {
181            match param {
182                ParamType::FixedBytes(32) => {
183                    if value.len() < 66 {
184                        let padded_value = [value, &"0".repeat(66 - value.len())].concat();
185                        token = if lenient {
186                            LenientTokenizer::tokenize(param, &padded_value)
187                        } else {
188                            StrictTokenizer::tokenize(param, &padded_value)
189                        };
190                    }
191                }
192                ParamType::Uint(_) => {
193                    // try again if value is hex
194                    if let Ok(value) = U256::from_str(value).map(|v| v.to_string()) {
195                        token = if lenient {
196                            LenientTokenizer::tokenize(param, &value)
197                        } else {
198                            StrictTokenizer::tokenize(param, &value)
199                        };
200                    }
201                }
202                // TODO: Not sure what to do here. Put the no effect in for now, but that is not
203                // ideal. We could attempt massage for every value type?
204                _ => {}
205            }
206        }
207
208        let token = token.map(sanitize_token)?;
209        tokens.push(token);
210    }
211    Ok(tokens)
212}
213
214/// Cleans up potential shortcomings of the ethabi Tokenizer.
215///
216/// For example: parsing a string array with a single empty string: `[""]`, is returned as
217///
218/// ```text
219///     [
220///        String(
221///            "\"\"",
222///        ),
223///    ],
224/// ```
225///
226/// But should just be
227///
228/// ```text
229///     [
230///        String(
231///            "",
232///        ),
233///    ],
234/// ```
235///
236/// This will handle this edge case
237/// > This function was taken from foundry.
238pub fn sanitize_token(token: Token) -> Token {
239    match token {
240        Token::Array(tokens) => {
241            let mut sanitized = Vec::with_capacity(tokens.len());
242            for token in tokens {
243                let token = match token {
244                    Token::String(val) => {
245                        let val = match val.as_str() {
246                            // this is supposed to be an empty string
247                            "\"\"" | "''" => "".to_owned(),
248                            _ => val,
249                        };
250                        Token::String(val)
251                    }
252                    _ => sanitize_token(token),
253                };
254                sanitized.push(token)
255            }
256            Token::Array(sanitized)
257        }
258        _ => token,
259    }
260}
261
262pub fn ec_add_function() -> Function {
263    #[allow(deprecated)]
264    Function {
265        name: "".to_owned(),
266        inputs: vec![
267            Param {
268                name: "".to_owned(),
269                kind: ParamType::Int(256),
270                internal_type: Some("sint256".to_owned()),
271            },
272            Param {
273                name: "".to_owned(),
274                kind: ParamType::Int(256),
275                internal_type: Some("sint256".to_owned()),
276            },
277            Param {
278                name: "".to_owned(),
279                kind: ParamType::Int(256),
280                internal_type: Some("sint256".to_owned()),
281            },
282            Param {
283                name: "".to_owned(),
284                kind: ParamType::Int(256),
285                internal_type: Some("sint256".to_owned()),
286            },
287        ],
288        outputs: vec![
289            Param {
290                name: "".to_owned(),
291                kind: ParamType::Int(256),
292                internal_type: Some("sint256".to_owned()),
293            },
294            Param {
295                name: "".to_owned(),
296                kind: ParamType::Int(256),
297                internal_type: Some("sint256".to_owned()),
298            },
299        ],
300        state_mutability: ethers::abi::StateMutability::Payable,
301        constant: None,
302    }
303}
304
305pub fn ec_mul_function() -> Function {
306    #[allow(deprecated)]
307    Function {
308        name: "".to_owned(),
309        inputs: vec![
310            Param {
311                name: "".to_owned(),
312                kind: ParamType::Int(256),
313                internal_type: Some("sint256".to_owned()),
314            },
315            Param {
316                name: "".to_owned(),
317                kind: ParamType::Int(256),
318                internal_type: Some("sint256".to_owned()),
319            },
320            Param {
321                name: "".to_owned(),
322                kind: ParamType::Uint(256),
323                internal_type: Some("uint256".to_owned()),
324            },
325        ],
326        outputs: vec![
327            Param {
328                name: "".to_owned(),
329                kind: ParamType::Int(256),
330                internal_type: Some("sint256".to_owned()),
331            },
332            Param {
333                name: "".to_owned(),
334                kind: ParamType::Int(256),
335                internal_type: Some("sint256".to_owned()),
336            },
337        ],
338        state_mutability: ethers::abi::StateMutability::Payable,
339        constant: None,
340    }
341}
342
343pub fn mod_exp_function() -> Function {
344    #[allow(deprecated)]
345    Function {
346        name: "".to_owned(),
347        inputs: vec![
348            Param {
349                name: "".to_owned(),
350                kind: ParamType::Int(256),
351                internal_type: Some("sint256".to_owned()),
352            },
353            Param {
354                name: "".to_owned(),
355                kind: ParamType::Int(256),
356                internal_type: Some("sint256".to_owned()),
357            },
358            Param {
359                name: "".to_owned(),
360                kind: ParamType::Int(256),
361                internal_type: Some("sint256".to_owned()),
362            },
363            Param {
364                name: "".to_owned(),
365                kind: ParamType::Bytes,
366                internal_type: Some("bytes".to_owned()),
367            },
368            Param {
369                name: "".to_owned(),
370                kind: ParamType::Bytes,
371                internal_type: Some("bytes".to_owned()),
372            },
373            Param {
374                name: "".to_owned(),
375                kind: ParamType::Bytes,
376                internal_type: Some("bytes".to_owned()),
377            },
378        ],
379        outputs: vec![Param {
380            name: "".to_owned(),
381            kind: ParamType::Bytes,
382            internal_type: Some("bytes".to_owned()),
383        }],
384        state_mutability: ethers::abi::StateMutability::Payable,
385        constant: None,
386    }
387}