1use alloy::{
2 network::EthereumWallet,
3 primitives::{Address, Bytes},
4 providers::ProviderBuilder,
5 signers::local::PrivateKeySigner,
6 transports::http::reqwest::Url,
7};
8use clap::{Parser, Subcommand};
9use eyre::Context;
10use newton_prover_chainio::{
11 policy_client::PolicyClientController, policy_client_registry::PolicyClientRegistryController,
12};
13use newton_prover_core::{config::NewtonAvsConfig, newton_policy_client::NewtonPolicyClient};
14use std::{path::PathBuf, str::FromStr};
15use tracing::info;
16
17use crate::config::NewtonCliConfig;
18
19#[derive(Debug, Parser)]
21#[command(name = "policy-client")]
22pub struct PolicyClientCommand {
23 #[command(subcommand)]
24 pub subcommand: PolicyClientSubcommand,
25}
26
27#[derive(Debug, Subcommand)]
28pub enum PolicyClientSubcommand {
29 #[command(name = "register")]
31 Register(RegisterCommand),
32
33 #[command(name = "deactivate")]
35 Deactivate(DeactivateCommand),
36
37 #[command(name = "activate")]
39 Activate(ActivateCommand),
40
41 #[command(name = "transfer-ownership")]
43 TransferOwnership(TransferOwnershipCommand),
44
45 #[command(name = "status")]
47 Status(StatusCommand),
48
49 #[command(name = "list")]
51 List(ListCommand),
52
53 #[command(name = "set-policy")]
55 SetPolicy(SetPolicyCommand),
56
57 #[command(name = "set-policy-params")]
59 SetPolicyParams(SetPolicyParamsCommand),
60}
61
62#[derive(Debug, Parser)]
64struct RegistryWriteArgs {
65 #[arg(long)]
67 registry: Address,
68
69 #[arg(long)]
71 client: Address,
72
73 #[arg(long, env = "PRIVATE_KEY")]
75 private_key: Option<String>,
76
77 #[arg(long, env = "RPC_URL")]
79 rpc_url: Option<String>,
80}
81
82impl RegistryWriteArgs {
83 fn validate(&self) -> eyre::Result<(String, String)> {
84 let private_key = self
85 .private_key
86 .clone()
87 .ok_or_else(|| eyre::eyre!("PRIVATE_KEY is required (set via --private-key or env var)"))?;
88 let rpc_url = self
89 .rpc_url
90 .clone()
91 .ok_or_else(|| eyre::eyre!("RPC_URL is required (set via --rpc-url or env var)"))?;
92 Ok((private_key, rpc_url))
93 }
94}
95
96#[derive(Debug, Parser)]
98pub struct RegisterCommand {
99 #[command(flatten)]
100 args: RegistryWriteArgs,
101}
102
103#[derive(Debug, Parser)]
105pub struct DeactivateCommand {
106 #[command(flatten)]
107 args: RegistryWriteArgs,
108}
109
110#[derive(Debug, Parser)]
112pub struct ActivateCommand {
113 #[command(flatten)]
114 args: RegistryWriteArgs,
115}
116
117#[derive(Debug, Parser)]
119pub struct TransferOwnershipCommand {
120 #[arg(long)]
122 registry: Address,
123
124 #[arg(long)]
126 client: Address,
127
128 #[arg(long)]
130 new_owner: Address,
131
132 #[arg(long, env = "PRIVATE_KEY")]
134 private_key: Option<String>,
135
136 #[arg(long, env = "RPC_URL")]
138 rpc_url: Option<String>,
139}
140
141#[derive(Debug, Parser)]
143pub struct StatusCommand {
144 #[arg(long)]
146 registry: Address,
147
148 #[arg(long)]
150 client: Address,
151
152 #[arg(long, env = "RPC_URL")]
154 rpc_url: Option<String>,
155}
156
157#[derive(Debug, Parser)]
159pub struct ListCommand {
160 #[arg(long)]
162 registry: Address,
163
164 #[arg(long)]
166 owner: Address,
167
168 #[arg(long, env = "RPC_URL")]
170 rpc_url: Option<String>,
171}
172
173#[derive(Debug, Parser)]
175pub struct SetPolicyCommand {
176 #[arg(long)]
178 client: Address,
179
180 #[arg(long)]
182 policy: Address,
183
184 #[arg(long, env = "PRIVATE_KEY")]
186 private_key: Option<String>,
187
188 #[arg(long, env = "RPC_URL")]
190 rpc_url: Option<String>,
191}
192
193#[derive(Debug, Parser)]
195pub struct SetPolicyParamsCommand {
196 #[arg(long)]
198 policy_client: Address,
199
200 #[arg(long)]
202 policy_params: PathBuf,
203
204 #[arg(long)]
206 expire_after: u32,
207
208 #[arg(long, env = "PRIVATE_KEY")]
209 private_key: Option<String>,
210
211 #[arg(long, env = "RPC_URL")]
212 rpc_url: Option<String>,
213}
214
215impl PolicyClientCommand {
216 pub async fn execute(self: Box<Self>, _config: NewtonAvsConfig<NewtonCliConfig>) -> eyre::Result<()> {
218 match self.subcommand {
219 PolicyClientSubcommand::Register(cmd) => {
220 let (private_key, rpc_url) = cmd.args.validate()?;
221 let controller = PolicyClientRegistryController::new(private_key, rpc_url, cmd.args.registry);
222 let receipt = controller
223 .register_client(cmd.args.client)
224 .await
225 .map_err(|e| eyre::eyre!("failed to register client: {}", e))?;
226 info!("Transaction hash: {}", receipt.transaction_hash);
227 Ok(())
228 }
229
230 PolicyClientSubcommand::Deactivate(cmd) => {
231 let (private_key, rpc_url) = cmd.args.validate()?;
232 let controller = PolicyClientRegistryController::new(private_key, rpc_url, cmd.args.registry);
233 let receipt = controller
234 .deactivate_client(cmd.args.client)
235 .await
236 .map_err(|e| eyre::eyre!("failed to deactivate client: {}", e))?;
237 info!("Transaction hash: {}", receipt.transaction_hash);
238 Ok(())
239 }
240
241 PolicyClientSubcommand::Activate(cmd) => {
242 let (private_key, rpc_url) = cmd.args.validate()?;
243 let controller = PolicyClientRegistryController::new(private_key, rpc_url, cmd.args.registry);
244 let receipt = controller
245 .activate_client(cmd.args.client)
246 .await
247 .map_err(|e| eyre::eyre!("failed to activate client: {}", e))?;
248 info!("Transaction hash: {}", receipt.transaction_hash);
249 Ok(())
250 }
251
252 PolicyClientSubcommand::TransferOwnership(cmd) => {
253 let private_key = cmd
254 .private_key
255 .ok_or_else(|| eyre::eyre!("PRIVATE_KEY is required (set via --private-key or env var)"))?;
256 let rpc_url = cmd
257 .rpc_url
258 .ok_or_else(|| eyre::eyre!("RPC_URL is required (set via --rpc-url or env var)"))?;
259
260 let controller = PolicyClientRegistryController::new(private_key, rpc_url, cmd.registry);
261 let receipt = controller
262 .set_client_owner(cmd.client, cmd.new_owner)
263 .await
264 .map_err(|e| eyre::eyre!("failed to transfer ownership: {}", e))?;
265 info!("Transaction hash: {}", receipt.transaction_hash);
266 Ok(())
267 }
268
269 PolicyClientSubcommand::Status(cmd) => {
270 let rpc_url = cmd
271 .rpc_url
272 .ok_or_else(|| eyre::eyre!("RPC_URL is required (set via --rpc-url or env var)"))?;
273
274 let controller = PolicyClientRegistryController::new_read_only(rpc_url, cmd.registry);
275
276 let is_active = controller
277 .is_registered_client(cmd.client)
278 .await
279 .map_err(|e| eyre::eyre!("failed to query registration status: {}", e))?;
280
281 match controller.get_client_record(cmd.client).await {
283 Ok(record) => {
284 println!("Client: {}", cmd.client);
285 println!(" Owner: {}", record.owner);
286 println!(" Active: {}", record.active);
287 println!(" Registered At: {} (unix timestamp)", record.registeredAt);
288 println!(" isRegisteredClient: {}", is_active);
289 }
290 Err(_) => {
291 println!("Client: {}", cmd.client);
292 println!(" Not registered in registry {}", cmd.registry);
293 }
294 }
295 Ok(())
296 }
297
298 PolicyClientSubcommand::List(cmd) => {
299 let rpc_url = cmd
300 .rpc_url
301 .ok_or_else(|| eyre::eyre!("RPC_URL is required (set via --rpc-url or env var)"))?;
302
303 let controller = PolicyClientRegistryController::new_read_only(rpc_url, cmd.registry);
304
305 let clients = controller
306 .get_clients_by_owner(cmd.owner)
307 .await
308 .map_err(|e| eyre::eyre!("failed to query clients: {}", e))?;
309
310 println!("Owner: {}", cmd.owner);
311 println!("Registered clients: {}", clients.len());
312 for client in &clients {
313 println!(" {}", client);
314 }
315 Ok(())
316 }
317
318 PolicyClientSubcommand::SetPolicy(cmd) => {
319 let private_key = cmd
320 .private_key
321 .ok_or_else(|| eyre::eyre!("PRIVATE_KEY is required (set via --private-key or env var)"))?;
322 let rpc_url = cmd
323 .rpc_url
324 .ok_or_else(|| eyre::eyre!("RPC_URL is required (set via --rpc-url or env var)"))?;
325
326 let private_key_str = private_key.strip_prefix("0x").unwrap_or(&private_key);
327 let signer = PrivateKeySigner::from_str(private_key_str).context("failed to parse private key")?;
328 let wallet = EthereumWallet::from(signer);
329 let url = Url::parse(&rpc_url).context("invalid RPC URL")?;
330 let provider = ProviderBuilder::new().wallet(wallet).connect_http(url);
331
332 let policy_client = NewtonPolicyClient::new(cmd.client, provider);
333
334 info!(
335 "Calling setPolicyAddress({}) on policy client {}",
336 cmd.policy, cmd.client
337 );
338
339 let tx = policy_client
340 .setPolicyAddress(cmd.policy)
341 .send()
342 .await
343 .context("failed to send setPolicyAddress transaction")?;
344
345 info!("Transaction sent: {:?}", tx.tx_hash());
346
347 let receipt = tx
348 .get_receipt()
349 .await
350 .context("failed to get setPolicyAddress receipt")?;
351
352 if !receipt.status() {
353 return Err(eyre::eyre!(
354 "setPolicyAddress transaction reverted (tx: {:?})",
355 receipt.transaction_hash
356 ));
357 }
358
359 info!("Policy client {} now points to policy {}", cmd.client, cmd.policy);
360 info!("Transaction hash: {}", receipt.transaction_hash);
361 Ok(())
362 }
363
364 PolicyClientSubcommand::SetPolicyParams(cmd) => {
365 let private_key = cmd
366 .private_key
367 .ok_or_else(|| eyre::eyre!("PRIVATE_KEY is required (set via --private-key or env var)"))?;
368 let rpc_url = cmd
369 .rpc_url
370 .ok_or_else(|| eyre::eyre!("RPC_URL is required (set via --rpc-url or env var)"))?;
371
372 info!("Setting policy params for policy client: {}", cmd.policy_client);
373
374 let policy_params_str = std::fs::read_to_string(&cmd.policy_params)
376 .with_context(|| format!("failed to read policy params file: {:?}", cmd.policy_params))?;
377
378 let _policy_params_json: serde_json::Value = serde_json::from_str(&policy_params_str)
380 .with_context(|| format!("failed to parse policy params as JSON: {:?}", cmd.policy_params))?;
381
382 let policy_params_bytes = Bytes::copy_from_slice(policy_params_str.as_bytes());
384
385 let controller = PolicyClientController::new(private_key, rpc_url, cmd.policy_client);
387 let (receipt, policy_id) = controller
388 .set_policy(policy_params_bytes, cmd.expire_after)
389 .await
390 .map_err(|e| eyre::eyre!("failed to set policy: {}", e))?;
391
392 info!("Policy set successfully");
393 info!("Transaction hash: {:?}", receipt.transaction_hash);
394 info!("Policy ID: {:?}", policy_id);
395
396 Ok(())
397 }
398 }
399 }
400}