newton_cli/commands/
policy.rs

1use alloy::primitives::Address;
2use clap::{Parser, Subcommand};
3use eyre::{Context, Result};
4use newton_prover_chainio::policy::PolicyController;
5use newton_prover_config::NewtonAvsConfig;
6use serde_json::Value;
7use std::path::PathBuf;
8use tracing;
9
10use crate::config::NewtonCliConfig;
11
12/// Policy commands
13#[derive(Debug, Parser)]
14#[command(name = "policy")]
15pub struct PolicyCommand {
16    #[command(subcommand)]
17    pub subcommand: PolicySubcommand,
18}
19
20#[derive(Debug, Subcommand)]
21pub enum PolicySubcommand {
22    /// Deploy policy
23    Deploy(DeployCommand),
24}
25
26fn validate_non_empty_path(s: &str) -> Result<PathBuf, String> {
27    if s.trim().is_empty() {
28        Err(String::from("Path cannot be empty"))
29    } else {
30        Ok(PathBuf::from(s))
31    }
32}
33
34/// Deploy policy command
35#[derive(Debug, Parser)]
36pub struct DeployCommand {
37    #[arg(long, env = "PRIVATE_KEY")]
38    private_key: Option<String>,
39
40    #[arg(long, env = "RPC_URL")]
41    rpc_url: Option<String>,
42
43    /// Address of the deployed policy data contract
44    #[arg(long)]
45    policy_data_address: Address,
46
47    #[arg(long, value_parser = validate_non_empty_path)]
48    policy_cids: PathBuf,
49}
50
51impl DeployCommand {
52    /// Deploy policy contract
53    ///
54    /// This function deploys a policy contract using the Rust chainio functions.
55    /// It returns the deployed policy address.
56    async fn deploy_policy(
57        private_key: &str,
58        rpc_url: &str,
59        policy_cid: &str,
60        schema_cid: &str,
61        entrypoint: &str,
62        policy_data_address: Address,
63        policy_metadata_cid: &str,
64    ) -> Result<Address> {
65        let controller = PolicyController::new(private_key.to_string(), rpc_url.to_string());
66
67        // Create policy_data array with the deployed policy data address
68        let policy_data = vec![policy_data_address];
69
70        tracing::info!(
71            "Deploying policy with entrypoint: {}, policyCid: {}, schemaCid: {}, policyData: {:?}, metadataCid: {}",
72            entrypoint,
73            policy_cid,
74            schema_cid,
75            policy_data,
76            policy_metadata_cid
77        );
78
79        let (_receipt, policy_address) = controller
80            .deploy_policy(
81                policy_cid.to_string(),
82                schema_cid.to_string(),
83                entrypoint.to_string(),
84                policy_data,
85                policy_metadata_cid.to_string(),
86            )
87            .await
88            .map_err(|e| eyre::eyre!("Failed to deploy policy: {}", e))?;
89
90        tracing::info!("Policy deployed successfully at address: {}", policy_address);
91
92        Ok(policy_address)
93    }
94
95    /// Execute the deploy command
96    pub async fn execute(self: Box<Self>, _config: NewtonAvsConfig<NewtonCliConfig>) -> eyre::Result<()> {
97        // Get values from args or env, with error if still missing
98        let private_key = self
99            .private_key
100            .ok_or_else(|| eyre::eyre!("private_key is required (use --private-key or PRIVATE_KEY env var)"))?;
101
102        let rpc_url = self
103            .rpc_url
104            .ok_or_else(|| eyre::eyre!("rpc_url is required (use --rpc-url or RPC_URL env var)"))?;
105
106        // Read from policy_cids.json file
107        let json_content = std::fs::read_to_string(&self.policy_cids)
108            .with_context(|| format!("Failed to read policy_cids.json: {:?}", self.policy_cids))?;
109        let json: Value = serde_json::from_str(&json_content)
110            .with_context(|| format!("Failed to parse policy_cids.json: {:?}", self.policy_cids))?;
111
112        // Extract values from policy_cids.json
113        let policy_cid = json.get("policyCid").and_then(|v| v.as_str()).unwrap_or("");
114        let schema_cid = json.get("schemaCid").and_then(|v| v.as_str()).unwrap_or("");
115        let entrypoint = json.get("entrypoint").and_then(|v| v.as_str()).unwrap_or("");
116        let policy_metadata_cid = json.get("policyMetadataCid").and_then(|v| v.as_str()).unwrap_or("");
117
118        // Deploy policy using the provided policy data address
119        let policy_address = Self::deploy_policy(
120            &private_key,
121            &rpc_url,
122            policy_cid,
123            schema_cid,
124            entrypoint,
125            self.policy_data_address,
126            policy_metadata_cid,
127        )
128        .await?;
129
130        tracing::info!("Policy Address: {}", policy_address);
131
132        Ok(())
133    }
134}
135
136impl PolicyCommand {
137    /// Execute the policy command
138    pub async fn execute(self: Box<Self>, config: NewtonAvsConfig<NewtonCliConfig>) -> eyre::Result<()> {
139        match self.subcommand {
140            PolicySubcommand::Deploy(command) => {
141                Box::new(command).execute(config).await?;
142            }
143        }
144        Ok(())
145    }
146}