Skip to content

Commit

Permalink
feat: support custom token deployers and switching off ethereum token (
Browse files Browse the repository at this point in the history
…#295)

# What ❔

This adds configuration options that are useful for custom bridge
deployments.
  • Loading branch information
montekki authored Nov 13, 2023
1 parent 8b7fa68 commit bfaabd4
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 13 deletions.
2 changes: 2 additions & 0 deletions .github/spellcheck/finalizer.dic
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ txs
unfinalizable
meterer
Timedout
newtype
JSON
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ Deployment is done by deploying a dockerized image of the service.
| `WITHDRAWAL_FINALIZER_ACCOUNT_PRIVATE_KEY` | The private key of the account that is going to be submit finalization transactions |
| `TX_RETRY_TIMEOUT_SECS` | Number of seconds to wait for a potentially stuck finalization transaction before readjusting its fees |
| `TOKENS_TO_FINALIZE` | Configures the sets of tokens this instance of finalizer will finalize. It may be configured as a whitelist, a blacklist, a wildcard or completely disable any finalization. For more info see below. |
| `FINALIZE_ETH_TOKEN` | (Optional) Configure, whether the Ethereum withdrawal events should be monitored. Useful to turn off for custom bridges that are only interested in a particular ERC20 token and have nothing to do with main Ethereum withdrawals |
| `CUSTOM_TOKEN_DEPLOYER_ADDRESSES` | (Optional) Normally ERC20 tokens are deployed by the bridge contract. However, in custom cases it may be necessary to override that behavior with a custom set of addresses that have deployed tokens |

The configuration structure describing the service config can be found in [`config.rs`](https://github.com/matter-labs/zksync-withdrawal-finalizer/blob/main/bin/withdrawal-finalizer/src/config.rs)

Expand Down
3 changes: 1 addition & 2 deletions bin/withdrawal-finalizer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ authors.workspace = true
[dependencies]
color-eyre = { workspace = true }
ethers = { workspace = true, default-features = false, features = ["ws", "rustls"] }
serde = { workspace = true }
tokio = { workspace = true, features = ["full"] }
url = { workspace = true, features = ["serde"] }
url = { workspace = true }
eyre = { workspace = true }
tokio-stream = { workspace = true }
envconfig = { workspace = true }
Expand Down
11 changes: 8 additions & 3 deletions bin/withdrawal-finalizer/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use envconfig::Envconfig;
use ethers::types::Address;
use finalizer::TokenList;
use serde::Deserialize;
use finalizer::{AddrList, TokenList};
use url::Url;

/// Withdrawal finalizer configuration.
///
/// Can be read from
/// * `env` via [`Self::init_from_env()`]
/// * TOML config file via [`Self::from_file()`]
#[derive(Deserialize, Debug, Envconfig)]
#[derive(Debug, Envconfig)]
pub struct Config {
/// L1 WS url.
#[envconfig(from = "ETH_CLIENT_WS_URL")]
Expand Down Expand Up @@ -66,4 +65,10 @@ pub struct Config {

#[envconfig(from = "TOKENS_TO_FINALIZE")]
pub tokens_to_finalize: Option<TokenList>,

#[envconfig(from = "FINALIZE_ETH_TOKEN")]
pub finalize_eth_token: Option<bool>,

#[envconfig(from = "CUSTOM_TOKEN_DEPLOYER_ADDRESSES")]
pub custom_token_deployer_addresses: Option<AddrList>,
}
6 changes: 5 additions & 1 deletion bin/withdrawal-finalizer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,12 @@ async fn main() -> Result<()> {

let l2_events = L2EventsListener::new(
config.api_web3_json_rpc_ws_url.as_str(),
config.l2_erc20_bridge_addr,
config
.custom_token_deployer_addresses
.map(|list| list.0)
.unwrap_or(vec![config.l2_erc20_bridge_addr]),
tokens.into_iter().collect(),
config.finalize_eth_token.unwrap_or(true),
);

let l1_bridge = IL1Bridge::new(config.l1_erc20_bridge_proxy_addr, client_l1.clone());
Expand Down
19 changes: 13 additions & 6 deletions chain-events/src/l2_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct NewTokenAdded;
/// A convenience multiplexer for withdrawal-related events.
pub struct L2EventsListener {
url: String,
l2_erc20_bridge_addr: Address,
token_deployer_addrs: Vec<Address>,
tokens: HashSet<Address>,
}

Expand Down Expand Up @@ -59,14 +59,21 @@ impl L2EventsListener {
/// # Arguments
///
/// * `middleware`: The middleware to perform requests with.
pub fn new(url: &str, l2_erc20_bridge_addr: Address, mut tokens: HashSet<Address>) -> Self {
tokens.insert(ETH_TOKEN_ADDRESS);
tokens.insert(ETH_ADDRESS);
pub fn new(
url: &str,
token_deployer_addrs: Vec<Address>,
mut tokens: HashSet<Address>,
finalize_eth_token: bool,
) -> Self {
if finalize_eth_token {
tokens.insert(ETH_TOKEN_ADDRESS);
tokens.insert(ETH_ADDRESS);
}
tokens.insert(DEPLOYER_ADDRESS);

Self {
url: url.to_string(),
l2_erc20_bridge_addr,
token_deployer_addrs,
tokens,
}
}
Expand Down Expand Up @@ -111,7 +118,7 @@ impl L2EventsListener {
.to_block(to_block)
.address(DEPLOYER_ADDRESS)
.topic0(vec![ContractDeployedFilter::signature()])
.topic1(self.l2_erc20_bridge_addr);
.topic1(self.token_deployer_addrs.clone());

// `get_logs` are used because there are not that many events
// expected and `get_logs_paginated` contains a bug that incorrectly
Expand Down
36 changes: 36 additions & 0 deletions finalizer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ impl FromStr for TokenList {
}
}

/// A newtype that represents a set of addresses in JSON format.
#[derive(Debug, Eq, PartialEq)]
pub struct AddrList(pub Vec<Address>);

impl FromStr for AddrList {
type Err = serde_json::Error;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let res: Vec<Address> = serde_json::from_str(s)?;
Ok(AddrList(res))
}
}

/// Finalizer.
pub struct Finalizer<M1, M2> {
pgpool: PgPool,
Expand Down Expand Up @@ -629,6 +642,10 @@ where

#[cfg(test)]
mod tests {
use std::str::FromStr;

use crate::AddrList;

use super::TokenList;
use ethers::abi::Address;
use pretty_assertions::assert_eq;
Expand Down Expand Up @@ -671,4 +688,23 @@ mod tests {
let allowed_usdc: TokenList = serde_json::from_str(white).unwrap();
assert_eq!(allowed_usdc, TokenList::WhiteList(vec![usdc_addr]));
}

#[test]
fn addr_list_de() {
let addr_1: Address = "0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4"
.parse()
.unwrap();
let addr_2: Address = "0x1820495E7d1B8BA82B706FB972d2A2B8282023d0"
.parse()
.unwrap();

let addr_list = r#"[
"0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4",
"0x1820495E7d1B8BA82B706FB972d2A2B8282023d0"
]"#;

let list: AddrList = AddrList::from_str(addr_list).unwrap();

assert_eq!(list, AddrList(vec![addr_1, addr_2]));
}
}

0 comments on commit bfaabd4

Please sign in to comment.