1use std::{ops::Sub, str::FromStr};
27
28use alloy::primitives::{Address, U256, utils::parse_units};
29use clap::Parser;
30use hopr_bindings::hoprtoken::HoprToken;
31use tracing::info;
32
33use crate::{
34 environment_config::NetworkProviderArgs,
35 key_pair::{ArgEnvReader, IdentityFileArgs, PrivateKeyArgs},
36 methods::{get_native_and_token_balances, transfer_native_tokens, transfer_or_mint_tokens},
37 utils::{Cmd, HelperErrors},
38};
39
40#[derive(Parser, Default, Debug)]
42pub struct FaucetArgs {
43 #[clap(flatten)]
45 pub network_provider: NetworkProviderArgs,
46
47 #[clap(
49 help = "Comma-separated Ethereum addresses of nodes that will receive funds",
50 long,
51 short,
52 default_value = None
53 )]
54 address: Option<String>,
55
56 #[clap(flatten)]
58 local_identity: IdentityFileArgs,
59
60 #[clap(
62 help = "Hopr amount in ether, e.g. 10",
63 long,
64 short = 't',
65 value_parser = clap::value_parser!(f64),
66 default_value_t = 2000.0
67 )]
68 hopr_amount: f64,
69
70 #[clap(
72 help = "Native token amount in ether, e.g. 1",
73 long,
74 short = 'g',
75 value_parser = clap::value_parser!(f64),
76 default_value_t = 10.0
77 )]
78 native_amount: f64,
79
80 #[clap(flatten)]
83 pub private_key: PrivateKeyArgs,
84}
85
86impl FaucetArgs {
87 async fn execute_faucet(self) -> Result<(), HelperErrors> {
89 let FaucetArgs {
90 network_provider,
91 address,
92 local_identity,
93 hopr_amount,
94 native_amount,
95 private_key,
96 } = self;
97
98 let mut eth_addresses_all: Vec<Address> = Vec::new();
100 if let Some(addresses) = address {
101 eth_addresses_all.extend(addresses.split(',').map(|addr| {
102 Address::from_str(addr).unwrap_or_else(|e| {
103 panic!(
104 "{}",
105 format!("Cannot parse address {:?} for eth_addresses_all, due to {:?}", addr, e)
106 )
107 })
108 }));
109 }
110 eth_addresses_all.extend(local_identity.to_addresses()?.into_iter().map(Address::from));
112 info!("All the addresses: {:?}", eth_addresses_all);
113
114 let signer_private_key = private_key.read_default()?;
116
117 let rpc_provider = network_provider.get_provider_with_signer(&signer_private_key).await?;
119 let contract_addresses = network_provider.get_network_details_from_name()?;
120
121 let hopr_token = HoprToken::new(contract_addresses.addresses.token.into(), rpc_provider.clone());
122
123 let (native_balances, token_balances) =
126 get_native_and_token_balances(hopr_token.clone(), eth_addresses_all.clone())
127 .await
128 .map_err(HelperErrors::MulticallError)?;
129 let hopr_token_amount: U256 = parse_units(&hopr_amount.to_string(), "ether")
131 .map_err(|_| HelperErrors::ParseError("Failed to parse hopr_amount units".into()))?
132 .into();
133
134 let hopr_token_amounts: Vec<U256> = vec![hopr_token_amount; eth_addresses_all.len()]
135 .into_iter()
136 .enumerate()
137 .map(|(i, h)| {
138 if h.gt(&token_balances[i]) {
139 let diff = h.sub(token_balances[i]);
140 info!(
141 "{:?} hopr-token to be transferred to {:?} to top up {:?}",
142 diff, eth_addresses_all[i], &token_balances[i]
143 );
144 diff
145 } else {
146 U256::ZERO
147 }
148 })
149 .collect();
150
151 let native_token_amount: U256 = parse_units(&native_amount.to_string(), "ether")
153 .map_err(|_| HelperErrors::ParseError("Failed to parse native_amount units".into()))?
154 .into();
155
156 let native_token_amounts: Vec<U256> = vec![native_token_amount; eth_addresses_all.len()]
157 .into_iter()
158 .enumerate()
159 .map(|(i, n)| {
160 if n.gt(&native_balances[i]) {
161 let diff = n.sub(native_balances[i]);
162 info!(
163 "{:?} native-token to be transferred to {:?}, to top up {:?}",
164 diff, eth_addresses_all[i], &native_balances[i]
165 );
166 diff
167 } else {
168 U256::ZERO
169 }
170 })
171 .collect();
172
173 let total_transferred_hopr_token =
175 transfer_or_mint_tokens(hopr_token, eth_addresses_all.clone(), hopr_token_amounts).await?;
176 info!("total transferred hopr-token is {:?}", total_transferred_hopr_token);
177 let total_transferred_native_token =
179 transfer_native_tokens(rpc_provider, eth_addresses_all, native_token_amounts).await?;
180 info!("total transferred native-token is {:?}", total_transferred_native_token);
181 Ok(())
182 }
183}
184
185impl Cmd for FaucetArgs {
186 fn run(self) -> Result<(), HelperErrors> {
188 Ok(())
189 }
190
191 async fn async_run(self) -> Result<(), HelperErrors> {
192 self.execute_faucet().await
193 }
194}