1use ethers::prelude::{Address, Http, LocalWallet, Provider, SignerMiddleware, U256};
2use std::{path::PathBuf, sync::Arc};
3use tdn::prelude::{Config as TdnConfig, PeerKey};
4use z4_types::{
5 env_value, env_values, hex_address, Network, NetworkConfig, Result, Z4_ROOM_MARKET_GROUP,
6};
7
8use crate::contracts::{RoomMarket, Token};
9
10#[derive(Default)]
12pub struct Config {
13 pub groups: Vec<u64>,
15 pub games: Vec<String>,
17 pub room_market: String,
19 pub secret_key: String,
21 pub ws_port: Option<u16>,
23 pub http_port: u16,
25 pub p2p_port: u16,
27 pub chain_network: String,
29 pub chain_rpcs: Vec<String>,
31 pub chain_start_block: Option<u64>,
33 pub auto_stake: bool,
35 pub url_http: String,
37 pub url_websocket: String,
39}
40
41impl Config {
42 pub fn from_env() -> Result<Self> {
44 dotenv::dotenv().ok();
45
46 let network = env_value("NETWORK", None)?;
47 let games: Vec<String> = env_values("GAMES", None)?;
48 let secret_key = env_value("SECRET_KEY", None)?;
49 let start_block = env_value("START_BLOCK", None).ok();
50
51 let chain_rpcs = env_values("RPC_ENDPOINTS", Some(vec![]))?;
52 let room_market = env_value("ROOM_MARKET", Some(games[0].clone()))?;
53 let url_http = env_value("URL_HTTP", Some("".to_owned()))?;
54 let url_websocket = env_value("URL_WEBSOCKET", Some("".to_owned()))?;
55 let http_port = env_value("HTTP_PORT", Some(8080))?;
56 let ws_port = env_value("WS_PORT", Some(8000))?;
57 let p2p_port = env_value("P2P_PORT", Some(7364))?;
58 let auto_stake = env_value("AUTO_STAKE", Some(false))?;
59
60 let mut config = Config::default();
61 config.http_port = http_port;
62 config.ws_port = Some(ws_port);
63 config.p2p_port = p2p_port;
64 config.secret_key = secret_key;
65 config.chain_network = network;
66 config.chain_rpcs = chain_rpcs;
67 config.chain_start_block = start_block;
68 config.games = games;
69 config.room_market = room_market;
70 config.auto_stake = auto_stake;
71 config.url_http = url_http;
72 config.url_websocket = url_websocket;
73
74 Ok(config)
75 }
76
77 pub fn to_tdn(&self) -> (TdnConfig, PeerKey) {
79 let rpc_addr = format!("0.0.0.0:{}", self.http_port).parse().unwrap();
80 let p2p_addr = format!("0.0.0.0:{}", self.p2p_port).parse().unwrap();
81 let mut config = TdnConfig::with_addr(p2p_addr, rpc_addr);
82 config.rpc_ws = match self.ws_port {
83 Some(port) => Some(format!("0.0.0.0:{}", port).parse().unwrap()),
84 None => None,
85 };
86 config.group_ids = self.groups.clone();
87 config.group_ids.push(Z4_ROOM_MARKET_GROUP);
88
89 let sk_str = self.secret_key.trim_start_matches("0x");
92 let sk_bytes = hex::decode(&sk_str).expect("Invalid secret key");
93 let key = PeerKey::from_db_bytes(&sk_bytes).expect("Invalid secret key");
94
95 config.db_path = Some(PathBuf::from(&format!("./.tdn/{:?}", key.peer_id())));
96
97 (config, key)
98 }
99
100 pub async fn to_chain(
102 &self,
103 ) -> Option<(
104 Vec<Arc<Provider<Http>>>,
105 Arc<SignerMiddleware<Arc<Provider<Http>>, LocalWallet>>,
106 Address,
107 Option<u64>,
108 )> {
109 if self.chain_network.is_empty() {
110 return None;
111 }
112
113 let network = Network::from_str(&self.chain_network);
114 let nc = NetworkConfig::from(network);
115 let rpcs = if self.chain_rpcs.is_empty() {
116 &nc.rpc_urls
117 } else {
118 &self.chain_rpcs
119 };
120 let providers: Vec<_> = rpcs
121 .iter()
122 .map(|rpc| Arc::new(Provider::<Http>::try_from(rpc).unwrap()))
123 .collect();
124 if providers.is_empty() {
125 panic!("NO RPCS");
126 }
127
128 let sk_str = self.secret_key.trim_start_matches("0x");
129 let signer = LocalWallet::from_bytes(&hex::decode(&sk_str).unwrap()).unwrap();
130 let signer_provider = Arc::new(
131 SignerMiddleware::new_with_provider_chain(providers[0].clone(), signer)
132 .await
133 .unwrap(),
134 );
135
136 let room_market = if self.room_market.is_empty() {
137 &self.games[0]
138 } else {
139 &self.room_market
140 };
141 let market_address = hex_address(room_market).expect("Invalid room market address");
142 if self.auto_stake && (!self.url_http.is_empty() || !self.url_websocket.is_empty()) {
143 let contract = RoomMarket::new(market_address, signer_provider.clone());
145 let token_address = contract.token().await.unwrap();
146 let token = Token::new(token_address, signer_provider.clone());
147
148 let amount = contract.min_staking().await.unwrap() * U256::from(100);
151 info!("Auto staking: {}", amount);
152 match token.approve(market_address, amount).send().await {
153 Ok(pending) => {
154 if let Ok(_receipt) = pending.await {
155 let _ = contract
156 .stake_sequencer(
157 self.url_http.clone(),
158 self.url_websocket.clone(),
159 amount,
160 )
161 .send()
162 .await;
163 } else {
164 error!("Failed to approve token");
165 }
166 }
167 Err(err) => {
168 if let Some(rcode) = err.decode_revert::<String>() {
169 error!("{}", rcode);
170 } else {
171 error!("{}", err);
172 }
173 }
174 }
175 }
176
177 Some((
178 providers,
179 signer_provider,
180 market_address,
181 self.chain_start_block,
182 ))
183 }
184}