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