1use crate::key_pair::{ArgEnvReader, ManagerPrivateKeyArgs};
98use crate::methods::{debug_node_safe_module_setup_main, debug_node_safe_module_setup_on_balance_and_registries};
99use crate::{
100 environment_config::NetworkProviderArgs,
101 key_pair::{IdentityFileArgs, PrivateKeyArgs},
102 methods::{
103 deploy_safe_module_with_targets_and_nodes, deregister_nodes_from_node_safe_registry_and_remove_from_module,
104 include_nodes_to_module, migrate_nodes, register_safes_and_nodes_on_network_registry, safe_singleton,
105 transfer_native_tokens, transfer_or_mint_tokens,
106 },
107 utils::{Cmd, HelperErrors},
108};
109use clap::{builder::RangedU64ValueParser, Parser};
110use ethers::{
111 types::{H160, U256},
112 utils::parse_units,
113};
114use hopr_bindings::{
115 hopr_network_registry::HoprNetworkRegistry, hopr_node_safe_registry::HoprNodeSafeRegistry,
116 hopr_node_stake_factory::HoprNodeStakeFactory, hopr_token::HoprToken,
117};
118use hopr_crypto_types::keypairs::Keypair;
119use safe_singleton::SafeSingleton;
120use std::str::FromStr;
121use tracing::{info, warn};
122
123#[derive(Clone, Debug, Parser)]
125pub enum SafeModuleSubcommands {
126 #[command(visible_alias = "cr")]
128 Create {
129 #[command(flatten)]
131 network_provider: NetworkProviderArgs,
132
133 #[command(flatten)]
135 local_identity: IdentityFileArgs,
136
137 #[clap(
139 help = "Comma separated node Ethereum addresses",
140 long,
141 short = 'o',
142 default_value = None
143 )]
144 node_address: Option<String>,
145
146 #[clap(
148 help = "Comma separated node Ethereum addresses",
149 long,
150 short = 'a',
151 default_value = None
152 )]
153 admin_address: Option<String>,
154
155 #[clap(
157 help = "Threshold for the generated safe, e.g. 1",
158 long,
159 short,
160 value_parser = RangedU64ValueParser::<u32>::new().range(1..),
161 default_value_t = 1
162 )]
163 threshold: u32,
164
165 #[clap(
167 help = "Provide the allowance of the channel contract to manage HOPR tokens on behalf of deployed safe. Value in ether, e.g. 10",
168 long,
169 short = 'l',
170 value_parser = clap::value_parser!(f64),
171 )]
172 allowance: Option<f64>,
173
174 #[clap(
176 help = "Hopr amount in ether, e.g. 10",
177 long,
178 short = 'm',
179 value_parser = clap::value_parser!(f64),
180 )]
181 hopr_amount: Option<f64>,
182
183 #[clap(
185 help = "Native token amount in ether, e.g. 1",
186 long,
187 short = 'g',
188 value_parser = clap::value_parser!(f64),
189 )]
190 native_amount: Option<f64>,
191
192 #[command(flatten)]
195 private_key: PrivateKeyArgs,
196
197 #[command(flatten, name = "manager_private_key")]
200 manager_private_key: ManagerPrivateKeyArgs,
201 },
202
203 #[command(visible_alias = "mg")]
205 Migrate {
206 #[command(flatten)]
208 network_provider: NetworkProviderArgs,
209
210 #[command(flatten)]
212 local_identity: IdentityFileArgs,
213
214 #[clap(
216 help = "Comma separated node Ethereum addresses",
217 long,
218 short = 'o',
219 default_value = None
220 )]
221 node_address: Option<String>,
222
223 #[clap(help = "New managing safe to which all the nodes move", long, short = 's')]
225 safe_address: String,
226
227 #[clap(help = "New managing module to which all the nodes move", long, short = 'm')]
229 module_address: String,
230
231 #[clap(
233 help = "Provide the allowance of the channel contract to manage HOPR tokens on behalf of deployed safe. Value in ether, e.g. 10",
234 long,
235 short = 'l',
236 value_parser = clap::value_parser!(f64),
237 )]
238 allowance: Option<f64>,
239
240 #[command(flatten)]
243 private_key: PrivateKeyArgs,
244
245 #[command(flatten, name = "manager_private_key")]
248 manager_private_key: ManagerPrivateKeyArgs,
249 },
250
251 #[command(visible_alias = "mv")]
253 Move {
254 #[command(flatten)]
256 network_provider: NetworkProviderArgs,
257
258 #[command(flatten)]
260 local_identity: IdentityFileArgs,
261
262 #[clap(
264 help = "Comma separated node Ethereum addresses",
265 long,
266 short = 'o',
267 default_value = None
268 )]
269 node_address: Option<String>,
270
271 #[clap(help = "Comma separated old module addresses", long, short = 'u')]
273 old_module_address: String,
274
275 #[clap(help = "New managing safe to which all the nodes move", long, short = 's')]
277 new_safe_address: String,
278
279 #[clap(help = "New managing module to which all the nodes move", long, short = 'm')]
281 new_module_address: String,
282
283 #[command(flatten)]
286 private_key: PrivateKeyArgs,
287
288 #[command(flatten, name = "manager_private_key")]
291 manager_private_key: ManagerPrivateKeyArgs,
292 },
293
294 #[command(visible_alias = "dg")]
296 Debug {
297 #[command(flatten)]
299 network_provider: NetworkProviderArgs,
300
301 #[command(flatten)]
303 local_identity: IdentityFileArgs,
304
305 #[clap(
307 help = "Comma separated node Ethereum addresses",
308 long,
309 short = 'o',
310 default_value = None
311 )]
312 node_address: Option<String>,
313
314 #[clap(help = "New managing safe to which all the nodes move", long, short = 's')]
316 safe_address: String,
317
318 #[clap(help = "New managing module to which all the nodes move", long, short = 'm')]
320 module_address: String,
321 },
322}
323
324impl SafeModuleSubcommands {
325 #[allow(clippy::too_many_arguments)]
334 pub async fn execute_safe_module_creation(
335 network_provider: NetworkProviderArgs,
336 local_identity: IdentityFileArgs,
337 node_address: Option<String>,
338 admin_address: Option<String>,
339 threshold: u32,
340 allowance: Option<f64>,
341 hopr_amount: Option<f64>,
342 native_amount: Option<f64>,
343 private_key: PrivateKeyArgs,
344 manager_private_key: ManagerPrivateKeyArgs,
345 ) -> Result<(), HelperErrors> {
346 let mut node_eth_addresses: Vec<H160> = Vec::new();
348 if let Some(addresses) = node_address {
349 node_eth_addresses.extend(
350 addresses
351 .split(',')
352 .map(|addr| {
353 H160::from_str(addr)
354 .map_err(|e| HelperErrors::InvalidAddress(format!("Invalid node address: {:?}", e)))
355 })
356 .collect::<Result<Vec<_>, _>>()?,
357 );
358 }
359 node_eth_addresses.extend(
361 local_identity
362 .to_addresses()
363 .map_err(|e| HelperErrors::InvalidAddress(format!("Invalid node address: {:?}", e)))?
364 .into_iter()
365 .map(H160::from),
366 );
367
368 let node_addresses = if node_eth_addresses.is_empty() {
369 None
370 } else {
371 Some(node_eth_addresses.clone())
372 };
373
374 let token_allowance = match allowance {
376 Some(allw) => U256::from(
377 parse_units(allw, "ether")
378 .map_err(|_| HelperErrors::ParseError("Failed to parse allowance units".into()))?,
379 ),
380 None => U256::max_value(),
381 };
382
383 let signer_private_key = private_key.read_default()?;
385 let rpc_provider = network_provider.get_provider_with_signer(&signer_private_key).await?;
387 let contract_addresses = network_provider.get_network_details_from_name()?;
388
389 let admin_eth_addresses: Vec<H160> = match admin_address {
391 Some(admin_address_str) => admin_address_str
392 .split(',')
393 .map(|addr| H160::from_str(addr).unwrap())
394 .collect(),
395 None => vec![signer_private_key.clone().public().to_address().into()],
396 };
397
398 let hopr_stake_factory =
406 HoprNodeStakeFactory::new(contract_addresses.addresses.node_stake_v2_factory, rpc_provider.clone());
407
408 let (safe, node_module) = deploy_safe_module_with_targets_and_nodes(
409 hopr_stake_factory,
410 contract_addresses.addresses.token.into(),
411 contract_addresses.addresses.channels.into(),
412 contract_addresses.addresses.module_implementation.into(),
413 contract_addresses.addresses.announcements.into(),
414 token_allowance,
415 node_addresses,
416 admin_eth_addresses,
417 U256::from(threshold),
418 )
419 .await?;
420
421 println!("safe {:?}", safe.address());
422 println!("node_module {:?}", node_module.address());
423
424 if let Some(hopr_amount_for_safe) = hopr_amount {
426 let hopr_token = HoprToken::new(contract_addresses.addresses.token, rpc_provider.clone());
427 let hopr_to_be_transferred = U256::from(
428 parse_units(hopr_amount_for_safe, "ether")
429 .map_err(|_| HelperErrors::ParseError("Failed to parse HOPR amount units".into()))?,
430 );
431
432 transfer_or_mint_tokens(hopr_token, vec![safe.address()], vec![hopr_to_be_transferred]).await?;
433 info!(
434 "safe {:?} has received {:?} HOPR tokens",
435 safe.address(),
436 hopr_amount_for_safe
437 );
438 }
439
440 if let Some(native_amount_for_node) = native_amount {
442 let native_to_be_transferred = U256::from(
443 parse_units(native_amount_for_node, "ether")
444 .map_err(|_| HelperErrors::ParseError("Failed to parse HOPR amount units".into()))?,
445 );
446 let native_amounts = vec![native_to_be_transferred; node_eth_addresses.len()];
447 transfer_native_tokens(rpc_provider.clone(), node_eth_addresses.clone(), native_amounts).await?;
448 info!(
449 "each node in {:?} has received {:?} native tokens",
450 node_eth_addresses, native_amount_for_node
451 );
452 }
453
454 if node_eth_addresses.is_empty() {
456 info!("No nodes provided. Skip actions around network registry");
457 return Ok(());
458 }
459
460 if let Ok(manager_private_key) = manager_private_key.read_default() {
462 let manager_rpc_provider = network_provider.get_provider_with_signer(&manager_private_key).await?;
464 let hopr_network_registry = HoprNetworkRegistry::new(
465 contract_addresses.addresses.network_registry,
466 manager_rpc_provider.clone(),
467 );
468 let (removed_pairs_num, added_pairs_num) = register_safes_and_nodes_on_network_registry(
471 hopr_network_registry,
472 vec![safe.address(); node_eth_addresses.len()],
473 node_eth_addresses.clone(),
474 )
475 .await?;
476 info!(
477 "{:?} pairs have been removed and {:?} pairs have been added to the network registry.",
478 removed_pairs_num, added_pairs_num
479 );
480 } else {
481 info!("skipping inclusion to the network registry as no manager private key has been provided.");
482 }
483
484 Ok(())
485 }
486
487 #[allow(clippy::too_many_arguments)]
491 pub async fn execute_safe_module_moving(
492 network_provider: NetworkProviderArgs,
493 local_identity: IdentityFileArgs,
494 node_address: Option<String>,
495 old_module_address: String,
496 new_safe_address: String,
497 new_module_address: String,
498 private_key: PrivateKeyArgs,
499 manager_private_key: ManagerPrivateKeyArgs,
500 ) -> Result<(), HelperErrors> {
501 let mut node_eth_addresses: Vec<H160> = Vec::new();
503 if let Some(addresses) = node_address {
504 node_eth_addresses.extend(
505 addresses
506 .split(',')
507 .map(|addr| {
508 H160::from_str(addr)
509 .map_err(|e| HelperErrors::InvalidAddress(format!("Invalid node address: {:?}", e)))
510 })
511 .collect::<Result<Vec<_>, _>>()?,
512 );
513 }
514 node_eth_addresses.extend(
516 local_identity
517 .to_addresses()
518 .map_err(|e| HelperErrors::InvalidAddress(format!("Invalid node address: {:?}", e)))?
519 .into_iter()
520 .map(H160::from),
521 );
522
523 let safe_addr = H160::from_str(&new_safe_address)
525 .map_err(|_| HelperErrors::InvalidAddress(format!("Cannot parse safe address {:?}", new_safe_address)))?;
526 let module_addr = H160::from_str(&new_module_address).map_err(|_| {
527 HelperErrors::InvalidAddress(format!("Cannot parse module address {:?}", new_module_address))
528 })?;
529 let old_module_addr: Vec<H160> = old_module_address
530 .split(',')
531 .map(|addr| H160::from_str(addr).unwrap())
532 .collect();
533
534 let signer_private_key = private_key.read_default()?;
536 let rpc_provider = network_provider.get_provider_with_signer(&signer_private_key).await?;
538 let contract_addresses = network_provider.get_network_details_from_name()?;
539
540 let hopr_node_safe_registry =
546 HoprNodeSafeRegistry::new(contract_addresses.addresses.node_safe_registry, rpc_provider.clone());
547 let safe = SafeSingleton::new(safe_addr, rpc_provider.clone());
548
549 if !node_eth_addresses.is_empty() {
550 match deregister_nodes_from_node_safe_registry_and_remove_from_module(
552 hopr_node_safe_registry.clone(),
553 node_eth_addresses.clone(),
554 old_module_addr,
555 signer_private_key.clone(),
556 )
557 .await
558 {
559 Ok(_) => {
560 info!("Nodes are deregistered from old safes");
561 }
562 Err(e) => {
563 return Err(e);
564 }
565 };
566
567 match include_nodes_to_module(
569 safe.clone(),
570 node_eth_addresses.clone(),
571 module_addr,
572 signer_private_key,
573 )
574 .await
575 {
576 Ok(_) => {
577 info!("Nodes are included to the new module");
578 }
579 Err(e) => {
580 return Err(e);
581 }
582 };
583 };
584
585 if node_eth_addresses.is_empty() {
587 info!("No nodes provided. Skip actions around network registry");
588 return Ok(());
589 }
590
591 if let Ok(manager_private_key) = manager_private_key.read_default() {
593 let manager_rpc_provider = network_provider.get_provider_with_signer(&manager_private_key).await?;
595 let hopr_network_registry = HoprNetworkRegistry::new(
596 contract_addresses.addresses.network_registry,
597 manager_rpc_provider.clone(),
598 );
599 let (removed_pairs_num, added_pairs_num) = register_safes_and_nodes_on_network_registry(
602 hopr_network_registry,
603 vec![safe.address(); node_eth_addresses.len()],
604 node_eth_addresses.clone(),
605 )
606 .await?;
607 info!(
608 "{:?} pairs have been removed and {:?} pairs have been added to the network registry.",
609 removed_pairs_num, added_pairs_num
610 );
611 } else {
612 info!("skipping inclusion to the network registry as no manager private key has been provided.");
613 }
614
615 Ok(())
616 }
617
618 #[allow(clippy::too_many_arguments)]
622 pub async fn execute_safe_module_migration(
623 network_provider: NetworkProviderArgs,
624 local_identity: IdentityFileArgs,
625 node_address: Option<String>,
626 safe_address: String,
627 module_address: String,
628 allowance: Option<f64>,
629 private_key: PrivateKeyArgs,
630 manager_private_key: ManagerPrivateKeyArgs,
631 ) -> Result<(), HelperErrors> {
632 let mut node_eth_addresses: Vec<H160> = Vec::new();
634 if let Some(addresses) = node_address {
635 node_eth_addresses.extend(
636 addresses
637 .split(',')
638 .map(|addr| {
639 H160::from_str(addr)
640 .map_err(|e| HelperErrors::InvalidAddress(format!("Invalid node address: {:?}", e)))
641 })
642 .collect::<Result<Vec<_>, _>>()?,
643 );
644 }
645 node_eth_addresses.extend(
647 local_identity
648 .to_addresses()
649 .map_err(|e| HelperErrors::InvalidAddress(format!("Invalid node address: {:?}", e)))?
650 .into_iter()
651 .map(H160::from),
652 );
653
654 let token_allowance = match allowance {
656 Some(allw) => U256::from(
657 parse_units(allw, "ether")
658 .map_err(|_| HelperErrors::ParseError("Failed to parse allowance units".into()))?,
659 ),
660 None => U256::max_value(),
661 };
662
663 let safe_addr = H160::from_str(&safe_address)
665 .map_err(|_| HelperErrors::InvalidAddress(format!("Cannot parse safe address {:?}", safe_address)))?;
666 let module_addr = H160::from_str(&module_address)
667 .map_err(|_| HelperErrors::InvalidAddress(format!("Cannot parse module address {:?}", module_address)))?;
668
669 let signer_private_key = private_key.read_default()?;
671 let rpc_provider = network_provider.get_provider_with_signer(&signer_private_key).await?;
673 let contract_addresses = network_provider.get_network_details_from_name()?;
674
675 let safe = SafeSingleton::new(safe_addr, rpc_provider.clone());
676
677 migrate_nodes(
682 safe.clone(),
683 module_addr,
684 contract_addresses.addresses.channels.into(),
685 contract_addresses.addresses.token.into(),
686 contract_addresses.addresses.announcements.into(),
687 token_allowance,
688 signer_private_key,
689 )
690 .await?;
691 info!("a new network has been included due to the migration");
692
693 if node_eth_addresses.is_empty() {
695 info!("No nodes provided. Skip actions around network registry");
696 return Ok(());
697 }
698
699 if let Ok(manager_private_key) = manager_private_key.read_default() {
701 let manager_rpc_provider = network_provider.get_provider_with_signer(&manager_private_key).await?;
703 let hopr_network_registry = HoprNetworkRegistry::new(
704 contract_addresses.addresses.network_registry,
705 manager_rpc_provider.clone(),
706 );
707 let (removed_pairs_num, added_pairs_num) = register_safes_and_nodes_on_network_registry(
710 hopr_network_registry,
711 vec![safe.address(); node_eth_addresses.len()],
712 node_eth_addresses.clone(),
713 )
714 .await?;
715 info!(
716 "{:?} pairs have been removed and {:?} pairs have been added to the network registry.",
717 removed_pairs_num, added_pairs_num
718 );
719 } else {
720 info!("skipping inclusion to the network registry as no manager private key has been provided.");
721 }
722 Ok(())
723 }
724
725 #[allow(clippy::too_many_arguments)]
736 pub async fn execute_safe_module_debugging(
737 network_provider: NetworkProviderArgs,
738 local_identity: IdentityFileArgs,
739 node_address: Option<String>,
740 safe_address: String,
741 module_address: String,
742 ) -> Result<(), HelperErrors> {
743 info!("Reading all the node addresses...");
745 let mut node_eth_addresses: Vec<H160> = Vec::new();
746 if let Some(addresses) = node_address {
747 node_eth_addresses.extend(
748 addresses
749 .split(',')
750 .map(|addr| {
751 H160::from_str(addr)
752 .map_err(|e| HelperErrors::InvalidAddress(format!("Invalid node address: {:?}", e)))
753 })
754 .collect::<Result<Vec<_>, _>>()?,
755 );
756 }
757 node_eth_addresses.extend(
759 local_identity
760 .to_addresses()
761 .map_err(|e| HelperErrors::InvalidAddress(format!("Invalid node address: {:?}", e)))?
762 .into_iter()
763 .map(H160::from),
764 );
765
766 let safe_addr = H160::from_str(&safe_address)
768 .map_err(|_| HelperErrors::InvalidAddress(format!("Cannot parse safe address {:?}", safe_address)))?;
769 let module_addr = H160::from_str(&module_address)
770 .map_err(|_| HelperErrors::InvalidAddress(format!("Cannot parse module address {:?}", module_address)))?;
771
772 let rpc_provider = network_provider.get_provider_without_signer().await?;
774 let contract_addresses = network_provider.get_network_details_from_name()?;
775
776 let hopr_token = HoprToken::new(contract_addresses.addresses.token, rpc_provider.clone());
777 let network_registry =
778 HoprNetworkRegistry::new(contract_addresses.addresses.network_registry, rpc_provider.clone());
779 let node_safe_registry =
780 HoprNodeSafeRegistry::new(contract_addresses.addresses.node_safe_registry, rpc_provider.clone());
781
782 for node in node_eth_addresses {
784 info!("Starting debug checks for node: {:?}", node);
785 info!("Checking node registration with network registry...");
786 let registered_safe = debug_node_safe_module_setup_on_balance_and_registries(
787 network_registry.clone(),
788 node_safe_registry.clone(),
789 &node,
790 )
791 .await
792 .map_err(|e| {
793 HelperErrors::MulticallError(
794 format!("failed in getting node balance their registration in network registry and node-safe registry: {:?}", e)
795 )
796 })?;
797
798 if registered_safe != safe_addr {
800 warn!(
801 "Node {:?} is not registered with the provided safe {:?}",
802 node, safe_addr
803 );
804 }
805 info!("Checking node and safe association in node-safe registry...");
806 debug_node_safe_module_setup_main(
807 hopr_token.clone(),
808 &module_addr,
809 &node,
810 &safe_addr,
811 &contract_addresses.addresses.channels.into(),
812 &contract_addresses.addresses.announcements.into(),
813 )
814 .await
815 .map_err(|e| HelperErrors::MulticallError(format!("failed in debugging safe and module: {:?}", e)))?;
816 }
817 Ok(())
818 }
819}
820
821impl Cmd for SafeModuleSubcommands {
822 fn run(self) -> Result<(), HelperErrors> {
824 Ok(())
826 }
827
828 async fn async_run(self) -> Result<(), HelperErrors> {
829 match self {
830 SafeModuleSubcommands::Create {
831 network_provider,
832 local_identity,
833 node_address,
834 admin_address,
835 threshold,
836 allowance,
837 hopr_amount,
838 native_amount,
839 private_key,
840 manager_private_key,
841 } => {
842 SafeModuleSubcommands::execute_safe_module_creation(
843 network_provider,
844 local_identity,
845 node_address,
846 admin_address,
847 threshold,
848 allowance,
849 hopr_amount,
850 native_amount,
851 private_key,
852 manager_private_key,
853 )
854 .await
855 }
856 SafeModuleSubcommands::Move {
857 network_provider,
858 local_identity,
859 node_address,
860 old_module_address,
861 new_safe_address,
862 new_module_address,
863 private_key,
864 manager_private_key,
865 } => {
866 SafeModuleSubcommands::execute_safe_module_moving(
867 network_provider,
868 local_identity,
869 node_address,
870 old_module_address,
871 new_safe_address,
872 new_module_address,
873 private_key,
874 manager_private_key,
875 )
876 .await
877 }
878 SafeModuleSubcommands::Migrate {
879 network_provider,
880 local_identity,
881 node_address,
882 safe_address,
883 module_address,
884 allowance,
885 private_key,
886 manager_private_key,
887 } => {
888 SafeModuleSubcommands::execute_safe_module_migration(
889 network_provider,
890 local_identity,
891 node_address,
892 safe_address,
893 module_address,
894 allowance,
895 private_key,
896 manager_private_key,
897 )
898 .await
899 }
900 SafeModuleSubcommands::Debug {
901 network_provider,
902 local_identity,
903 node_address,
904 safe_address,
905 module_address,
906 } => {
907 SafeModuleSubcommands::execute_safe_module_debugging(
908 network_provider,
909 local_identity,
910 node_address,
911 safe_address,
912 module_address,
913 )
914 .await
915 }
916 }
917 }
918}