sui_cluster_test/
faucet.rs1use super::cluster::{Cluster, new_wallet_context_from_cluster};
5use async_trait::async_trait;
6use fastcrypto::encoding::{Encoding, Hex};
7use std::collections::HashMap;
8use std::env;
9use std::sync::Arc;
10use sui_faucet::{FaucetConfig, FaucetResponse, LocalFaucet, RequestStatus};
11use sui_types::base_types::SuiAddress;
12use sui_types::crypto::KeypairTraits;
13use tracing::{Instrument, debug, info, info_span};
14
15pub struct FaucetClientFactory;
16
17impl FaucetClientFactory {
18 pub async fn new_from_cluster(
19 cluster: &(dyn Cluster + Sync + Send),
20 ) -> Arc<dyn FaucetClient + Sync + Send> {
21 match cluster.remote_faucet_url() {
22 Some(url) => Arc::new(RemoteFaucetClient::new(url.into())),
23 None => {
25 let key = cluster
26 .local_faucet_key()
27 .expect("Expect local faucet key for local cluster")
28 .copy();
29 let wallet_context = new_wallet_context_from_cluster(cluster, key)
30 .await
31 .instrument(info_span!("init_wallet_context_for_faucet"));
32
33 let config = FaucetConfig::default();
34 let simple_faucet = LocalFaucet::new(wallet_context.into_inner(), config)
35 .await
36 .unwrap();
37
38 Arc::new(LocalFaucetClient::new(simple_faucet))
39 }
40 }
41 }
42}
43
44#[async_trait]
46pub trait FaucetClient {
47 async fn request_sui_coins(&self, request_address: SuiAddress) -> FaucetResponse;
48}
49
50pub struct RemoteFaucetClient {
52 remote_url: String,
53}
54
55impl RemoteFaucetClient {
56 fn new(url: String) -> Self {
57 info!("Use remote faucet: {}", url);
58 Self { remote_url: url }
59 }
60}
61
62#[async_trait]
63impl FaucetClient for RemoteFaucetClient {
64 async fn request_sui_coins(&self, request_address: SuiAddress) -> FaucetResponse {
67 let gas_url = format!("{}/v2/gas", self.remote_url);
68 debug!("Getting coin from remote faucet {}", gas_url);
69 let data = HashMap::from([("recipient", Hex::encode(request_address))]);
70 let map = HashMap::from([("FixedAmountRequest", data)]);
71
72 let auth_header = match env::var("FAUCET_AUTH_HEADER") {
73 Ok(val) => val,
74 _ => "".to_string(),
75 };
76
77 let response = reqwest::Client::new()
78 .post(&gas_url)
79 .header("Authorization", auth_header)
80 .json(&map)
81 .send()
82 .await
83 .unwrap_or_else(|e| panic!("Failed to talk to remote faucet {:?}: {:?}", gas_url, e));
84 let full_bytes = response.bytes().await.unwrap();
85 let faucet_response: FaucetResponse = serde_json::from_slice(&full_bytes)
86 .map_err(|e| anyhow::anyhow!("json deser failed with bytes {:?}: {e}", full_bytes))
87 .unwrap();
88
89 if let RequestStatus::Failure(error) = &faucet_response.status {
90 panic!("Failed to get gas tokens with error: {}", error)
91 };
92
93 faucet_response
94 }
95}
96
97pub struct LocalFaucetClient {
99 simple_faucet: Arc<LocalFaucet>,
100}
101
102impl LocalFaucetClient {
103 fn new(simple_faucet: Arc<LocalFaucet>) -> Self {
104 info!("Use local faucet");
105 Self { simple_faucet }
106 }
107}
108#[async_trait]
109impl FaucetClient for LocalFaucetClient {
110 async fn request_sui_coins(&self, request_address: SuiAddress) -> FaucetResponse {
111 let coins = self
112 .simple_faucet
113 .local_request_execute_tx(request_address)
114 .await
115 .unwrap_or_else(|err| panic!("Failed to get gas tokens with error: {}", err));
116
117 FaucetResponse {
118 status: RequestStatus::Success,
119 coins_sent: Some(coins),
120 }
121 }
122}