1mod simple_faucet;
6mod write_ahead_log;
7
8use std::{net::Ipv4Addr, path::PathBuf, sync::Arc};
9
10use async_trait::async_trait;
11use clap::Parser;
12use iota_types::base_types::{IotaAddress, ObjectID, TransactionDigest};
13use serde::{Deserialize, Serialize};
14use uuid::Uuid;
15
16pub use self::simple_faucet::SimpleFaucet;
17use crate::FaucetError;
18
19#[derive(Serialize, Deserialize, Debug, Clone)]
20pub struct FaucetReceipt {
21 pub sent: Vec<CoinInfo>,
22}
23
24#[derive(Serialize, Deserialize, Debug, Clone)]
25pub struct BatchFaucetReceipt {
26 pub task: String,
27}
28
29#[derive(Serialize, Deserialize, Debug, Clone)]
30#[serde(rename_all = "camelCase")]
31pub struct CoinInfo {
32 pub amount: u64,
33 pub id: ObjectID,
34 pub transfer_tx_digest: TransactionDigest,
35}
36
37#[derive(Serialize, Deserialize, Debug, Clone)]
38pub struct BatchSendStatus {
39 pub status: BatchSendStatusType,
40 pub transferred_gas_objects: Option<FaucetReceipt>,
41}
42
43#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
44#[serde(rename_all = "UPPERCASE")]
45pub enum BatchSendStatusType {
46 INPROGRESS,
47 SUCCEEDED,
48 DISCARDED,
49}
50
51pub struct AppState<F = Arc<SimpleFaucet>> {
52 pub faucet: F,
53 pub config: FaucetConfig,
54}
55
56impl<F> AppState<F> {
57 pub fn new(faucet: F, config: FaucetConfig) -> Self {
58 Self { faucet, config }
59 }
60}
61
62#[async_trait]
63pub trait Faucet {
64 async fn send(
66 &self,
67 id: Uuid,
68 recipient: IotaAddress,
69 amounts: &[u64],
70 ) -> Result<FaucetReceipt, FaucetError>;
71
72 async fn batch_send(
75 &self,
76 id: Uuid,
77 recipient: IotaAddress,
78 amounts: &[u64],
79 ) -> Result<BatchFaucetReceipt, FaucetError>;
80
81 async fn get_batch_send_status(&self, task_id: Uuid) -> Result<BatchSendStatus, FaucetError>;
83
84 async fn rate_limit(&self, recipient: IotaAddress) -> Result<(), FaucetError>;
86}
87
88pub const DEFAULT_AMOUNT: u64 = 1_000_000_000;
89pub const DEFAULT_NUM_OF_COINS: usize = 1;
90
91#[derive(Parser, Clone)]
92#[command(
93 name = "IOTA Faucet",
94 about = "Faucet for requesting test tokens on IOTA"
95)]
96pub struct FaucetConfig {
97 #[arg(long, default_value_t = 5003)]
98 pub port: u16,
99
100 #[arg(long, default_value = "127.0.0.1")]
101 pub host_ip: Ipv4Addr,
102
103 #[arg(long, default_value_t = DEFAULT_AMOUNT)]
104 pub amount: u64,
105
106 #[arg(long, default_value_t = DEFAULT_NUM_OF_COINS)]
107 pub num_coins: usize,
108
109 #[arg(long, default_value_t = 10)]
110 pub request_buffer_size: usize,
111
112 #[arg(long, default_value_t = 10)]
113 pub max_request_per_second: u64,
114
115 #[arg(long, default_value_t = 60)]
116 pub wallet_client_timeout_secs: u64,
117
118 #[arg(long)]
119 pub write_ahead_log: PathBuf,
120
121 #[arg(long, default_value_t = 300)]
122 pub wal_retry_interval: u64,
123
124 #[arg(long, default_value_t = 10000)]
125 pub max_request_queue_length: u64,
126
127 #[arg(long, default_value_t = 500)]
128 pub batch_request_size: u64,
129
130 #[arg(long, default_value_t = 300)]
131 pub ttl_expiration: u64,
132
133 #[arg(long, action = clap::ArgAction::Set, default_value_t = false)]
134 pub batch_enabled: bool,
135
136 #[arg(long, default_value_t = false)]
137 pub enable_rate_limiting: bool,
138
139 #[arg(long, default_value_t = 12)]
140 pub max_requests_per_window: usize,
141
142 #[arg(long, default_value_t = 43200)]
144 pub rate_window_secs: u64,
145}
146
147impl Default for FaucetConfig {
148 fn default() -> Self {
149 Self {
150 port: 5003,
151 host_ip: Ipv4Addr::new(127, 0, 0, 1),
152 amount: DEFAULT_AMOUNT,
153 num_coins: DEFAULT_NUM_OF_COINS,
154 request_buffer_size: 10,
155 max_request_per_second: 10,
156 wallet_client_timeout_secs: 60,
157 write_ahead_log: Default::default(),
158 wal_retry_interval: 300,
159 max_request_queue_length: 10000,
160 batch_request_size: 500,
161 ttl_expiration: 300,
162 batch_enabled: false,
163 enable_rate_limiting: false,
164 max_requests_per_window: 12,
165 rate_window_secs: 12,
166 }
167 }
168}