decdn_common/address.rs
1//! Shared Ethereum address parsing for the deCDN binaries.
2//!
3//! Both the daemon (`decdn-node`) and the CLI (`decdn`) parse contract and
4//! account addresses out of flags and `[blockchain]` config. This module is the
5//! single home for the two parse helpers so the zero-address guard cannot drift
6//! between binaries. It lives in `decdn-common` because that is the one crate on
7//! the shared path of everything that needs it — `node → common`, `cli → common`,
8//! and `incentive → common` — and it already depends on `alloy` (the
9//! `decdn-config-types` leaf crate deliberately does not, to keep `alloy` off the
10//! publisher-CLI path, so the guard cannot live there).
11
12use alloy::primitives::Address;
13use anyhow::Context;
14
15/// Parse a contract/account address with a labelled error.
16pub fn parse_address(value: &str, label: &str) -> anyhow::Result<Address> {
17 value
18 .parse()
19 .with_context(|| format!("{label} {value:?} is not a valid address"))
20}
21
22/// Parse a contract address and reject the zero address. `Address::ZERO` parses
23/// cleanly but is never a real deployment — it would surface only as an opaque
24/// on-chain revert at call time, so reject it here with a clear, labelled error.
25///
26/// Use this for **every contract address** parsed from a flag/config: the CLI's
27/// `resolve` / `fetch` / `pool` / `setup` sites (#1153/#1213), the daemon's
28/// runtime bring-up sites (#1219), and the swap-venue addresses in
29/// `decdn-incentive` (#1213). Account/EOA addresses (`--provider-address`,
30/// `operator`) deliberately stay on [`parse_address`] — the "never a real
31/// deployment" rationale is contract-specific.
32pub fn parse_nonzero_address(value: &str, label: &str) -> anyhow::Result<Address> {
33 let addr = parse_address(value, label)?;
34 anyhow::ensure!(
35 addr != Address::ZERO,
36 "{label} must not be the zero address — set it to the deployed contract address"
37 );
38 Ok(addr)
39}
40
41#[cfg(test)]
42#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
43mod tests;