Skip to main content

espresso_contract_deployer/proposals/
proposal_toml.rs

1//! `proposal.toml` schema: generated by `deploy`, validated by `deploy verify-proposal`.
2
3use alloy::primitives::{Address, B256, U256};
4use anyhow::{Context, Result};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
8pub struct ProposalToml {
9    /// Contract kind in kebab-case (e.g. "stake-table-v3").
10    pub contract: String,
11    /// Network name (e.g. "decaf", "mainnet").
12    pub network: String,
13    /// EVM chain ID.
14    pub chain_id: u64,
15    /// Proxy contract address.
16    pub proxy: Address,
17    /// New implementation address (from the inner upgradeToAndCall).
18    #[serde(rename = "impl")]
19    pub new_impl: Address,
20    /// Timelock contract address (outer_to from the proposal).
21    pub timelock: Address,
22    /// Salt used in the timelock operation.
23    pub salt: B256,
24    /// Timelock delay in seconds.
25    pub delay: u64,
26    /// Predecessor (zero for independent operations).
27    pub predecessor: B256,
28
29    pub schedule: PhaseToml,
30    pub execute: PhaseToml,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
34pub struct PhaseToml {
35    /// Safe multisig address that signs this phase.
36    pub safe: Address,
37    /// Safe nonce at proposal-generation time.
38    pub nonce: u64,
39    /// EIP-712 domain separator hash.
40    pub domain: B256,
41    /// EIP-712 message hash.
42    pub message: B256,
43    /// EIP-712 safe_tx hash (what the Ledger displays).
44    pub safe_tx: B256,
45}
46
47impl ProposalToml {
48    pub fn load(dir: &std::path::Path) -> Result<Self> {
49        let p = dir.join("proposal.toml");
50        let text =
51            std::fs::read_to_string(&p).with_context(|| format!("cannot read {}", p.display()))?;
52        toml::from_str(&text).with_context(|| format!("failed to parse {}", p.display()))
53    }
54
55    pub fn write(&self, dir: &std::path::Path) -> Result<()> {
56        let p = dir.join("proposal.toml");
57        let text = toml::to_string_pretty(self).context("failed to serialize proposal.toml")?;
58        // Prepend generation notice.
59        let full = format!(
60            "# Generated by `deploy`; validated by `deploy verify-proposal` and CI. Do not \
61             hand-edit.\n{text}"
62        );
63        std::fs::write(&p, full).with_context(|| format!("failed to write {}", p.display()))
64    }
65
66    /// Convert the TOML delay (u64 seconds) to a U256 for comparison with on-chain values.
67    pub fn delay_u256(&self) -> U256 {
68        U256::from(self.delay)
69    }
70
71    /// Convert the TOML predecessor B256 field.
72    pub fn predecessor_b256(&self) -> B256 {
73        self.predecessor
74    }
75}