main.rs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. use std::process::exit;
  2. use clap::{IntoApp, Parser, Subcommand};
  3. use prettytable::{format, row, Table};
  4. use url::Url;
  5. use darkfi::{rpc::client::RpcClient, Result};
  6. mod rpc;
  7. #[derive(Subcommand)]
  8. pub enum CliDaoSubCommands {
  9. /// Create DAO
  10. Create {
  11. /// Minium number of governance tokens a user must have to propose a vote.
  12. dao_proposer_limit: u64,
  13. /// Minimum number of governance tokens staked on a proposal for it to pass.
  14. dao_quorum: u64,
  15. /// Quotient value of minimum vote ratio of yes:no votes required for a proposal to pass.
  16. dao_approval_ratio_quot: u64,
  17. /// Base value of minimum vote ratio of yes:no votes required for a proposal to pass.
  18. dao_approval_ratio_base: u64,
  19. },
  20. /// Get DAO public address.
  21. Addr {},
  22. /// Get votes on current proposal in the form of [[true/false, user's GOV_tokens],[...]].
  23. GetVotes {},
  24. /// Get proposals in the form of [[destination, amount, token_id], [...]].
  25. GetProposals {},
  26. /// Mint tokens.
  27. Mint {
  28. /// Number of treasury tokens to mint.
  29. token_supply: u64,
  30. /// Public key of the DAO treasury.
  31. dao_addr: String,
  32. },
  33. /// Get user balance.
  34. UserBalance {
  35. /// User public address.
  36. addr: String,
  37. },
  38. /// Get DAO treasury balance.
  39. DaoBalance {},
  40. /// Get DAO bulla.
  41. DaoBulla {},
  42. /// Generate a new PublicKey.
  43. Keygen {},
  44. /// Airdrop tokens given recipient address and value.
  45. Airdrop {
  46. /// Airdrop recipient address.
  47. addr: String,
  48. /// Value to be airdropped.
  49. value: u64,
  50. },
  51. /// Create a Proposal.
  52. Propose {
  53. /// Sender PublicKey.
  54. sender: String,
  55. /// Recipient PublicKey.
  56. recipient: String,
  57. /// Amount of tokens to be sent.
  58. amount: u64,
  59. },
  60. /// Vote
  61. Vote {
  62. /// Voter's public address.
  63. addr: String,
  64. /// Vote value [yes/no].
  65. vote: String,
  66. },
  67. /// Execute proposal bulla.
  68. Exec {
  69. /// Bulla.
  70. bulla: String,
  71. },
  72. }
  73. /// DAO cli
  74. #[derive(Parser)]
  75. #[clap(name = "dao")]
  76. #[clap(arg_required_else_help(true))]
  77. pub struct CliDao {
  78. /// Increase verbosity
  79. #[clap(short, parse(from_occurrences))]
  80. pub verbose: u8,
  81. #[clap(subcommand)]
  82. pub command: Option<CliDaoSubCommands>,
  83. }
  84. pub struct Rpc {
  85. client: RpcClient,
  86. }
  87. async fn start(options: CliDao) -> Result<()> {
  88. let rpc_addr = "tcp://127.0.0.1:7777";
  89. let client = Rpc { client: RpcClient::new(Url::parse(rpc_addr)?).await? };
  90. match options.command {
  91. Some(CliDaoSubCommands::Create {
  92. dao_proposer_limit,
  93. dao_quorum,
  94. dao_approval_ratio_base,
  95. dao_approval_ratio_quot,
  96. }) => {
  97. let reply = client
  98. .create(
  99. dao_proposer_limit,
  100. dao_quorum,
  101. dao_approval_ratio_quot,
  102. dao_approval_ratio_base,
  103. )
  104. .await?;
  105. println!("Created DAO bulla: {}", &reply.to_string());
  106. return Ok(())
  107. }
  108. Some(CliDaoSubCommands::Addr {}) => {
  109. let reply = client.addr().await?;
  110. println!("DAO public address: {}", &reply.to_string());
  111. return Ok(())
  112. }
  113. Some(CliDaoSubCommands::GetVotes {}) => {
  114. let reply = client.get_votes().await?;
  115. println!("Votes on current proposals: {}", &reply.to_string());
  116. return Ok(())
  117. }
  118. Some(CliDaoSubCommands::GetProposals {}) => {
  119. let reply = client.get_proposals().await?;
  120. println!("Current proposals: {}", &reply.to_string());
  121. return Ok(())
  122. }
  123. Some(CliDaoSubCommands::Mint { token_supply, dao_addr }) => {
  124. let reply = client.mint(token_supply, dao_addr).await?;
  125. println!("{}", &reply.as_str().unwrap().to_string());
  126. return Ok(())
  127. }
  128. Some(CliDaoSubCommands::Keygen {}) => {
  129. let reply = client.keygen().await?;
  130. println!("User public key: {}", &reply.to_string());
  131. return Ok(())
  132. }
  133. Some(CliDaoSubCommands::Airdrop { addr, value }) => {
  134. println!("Requesting airdrop of {} GOV", value);
  135. let reply = client.airdrop(addr, value).await?;
  136. println!("{}", &reply.as_str().unwrap().to_string());
  137. return Ok(())
  138. }
  139. Some(CliDaoSubCommands::DaoBalance {}) => {
  140. let rep = client.dao_balance().await?;
  141. if !rep.is_object() {
  142. eprintln!("Invalid balance data received from darkfid RPC endpoint.");
  143. exit(1);
  144. }
  145. let mut table = Table::new();
  146. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  147. table.set_titles(row!["Token", "Balance"]);
  148. for i in rep.as_object().unwrap().keys() {
  149. if let Some(balance) = rep[i].as_u64() {
  150. table.add_row(row![i, balance]);
  151. continue
  152. }
  153. eprintln!("Found invalid balance data for key \"{}\"", i);
  154. }
  155. if table.is_empty() {
  156. println!("No balances.");
  157. } else {
  158. println!("{}", table);
  159. }
  160. // println!("DAO balance: {}", &reply.to_string());
  161. return Ok(())
  162. }
  163. Some(CliDaoSubCommands::DaoBulla {}) => {
  164. let reply = client.dao_bulla().await?;
  165. println!("DAO bulla: {}", &reply.to_string());
  166. return Ok(())
  167. }
  168. Some(CliDaoSubCommands::UserBalance { addr }) => {
  169. let rep = client.user_balance(addr).await?;
  170. if !rep.is_object() {
  171. eprintln!("Invalid balance data received from darkfid RPC endpoint.");
  172. exit(1);
  173. }
  174. let mut table = Table::new();
  175. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  176. table.set_titles(row!["Token", "Balance"]);
  177. for i in rep.as_object().unwrap().keys() {
  178. if let Some(balance) = rep[i].as_u64() {
  179. table.add_row(row![i, balance]);
  180. continue
  181. }
  182. eprintln!("Found invalid balance data for key \"{}\"", i);
  183. }
  184. if table.is_empty() {
  185. println!("No balances.");
  186. } else {
  187. println!("{}", table);
  188. }
  189. // println!("User balance: {}", &reply.to_string());
  190. return Ok(())
  191. }
  192. Some(CliDaoSubCommands::Propose { sender, recipient, amount }) => {
  193. let reply = client.propose(sender.clone(), recipient.clone(), amount).await?;
  194. println!(
  195. "Proposal bulla: {}\nSender: {}\nRecipient: {}\nAmount: {} DRK",
  196. &reply.to_string(),
  197. sender,
  198. recipient,
  199. amount
  200. );
  201. return Ok(())
  202. }
  203. Some(CliDaoSubCommands::Vote { addr, vote }) => {
  204. let reply = client.vote(addr, vote).await?;
  205. println!("{}", &reply.to_string());
  206. return Ok(())
  207. }
  208. Some(CliDaoSubCommands::Exec { bulla }) => {
  209. let reply = client.exec(bulla).await?;
  210. println!("{}", &reply.to_string());
  211. return Ok(())
  212. }
  213. None => {}
  214. }
  215. Ok(())
  216. }
  217. #[async_std::main]
  218. async fn main() -> Result<()> {
  219. let args = CliDao::parse();
  220. let _matches = CliDao::command().get_matches();
  221. //let config_path = if args.config.is_some() {
  222. // expand_path(&args.config.clone().unwrap())?
  223. //} else {
  224. // join_config_path(&PathBuf::from("drk.toml"))?
  225. //};
  226. // Spawn config file if it's not in place already.
  227. //spawn_config(&config_path, CONFIG_FILE_CONTENTS)?;
  228. //let (lvl, conf) = log_config(matches)?;
  229. //TermLogger::init(lvl, conf, TerminalMode::Mixed, ColorChoice::Auto)?;
  230. //let config = Config::<DrkConfig>::load(config_path)?;
  231. start(args).await
  232. }