layer_climb_cli/command/
wallet.rs1use anyhow::Result;
2use bip39::Mnemonic;
3use clap::Subcommand;
4use layer_climb::{prelude::*, proto::abci::TxResponse};
5use rand::Rng;
6
7#[derive(Debug, Clone, Subcommand)]
8pub enum WalletCommand {
9 Create,
11 Show,
13 Balance {
15 #[arg(long)]
16 address: String,
18 #[arg(long)]
20 denom: Option<String>,
21 },
22 AllBalances {
23 #[arg(long)]
24 address: String,
26 },
27 Transfer {
29 #[arg(long)]
30 to: String,
32 #[arg(long)]
34 amount: u128,
35 #[arg(long)]
37 denom: Option<String>,
38 },
39}
40
41impl WalletCommand {
42 pub async fn run(
43 &self,
44 client: impl Into<AnyClient>,
45 rng: &mut impl Rng,
46 log: impl Fn(WalletLog),
47 ) -> Result<()> {
48 let client = client.into();
49 match self {
50 WalletCommand::Create => {
51 let (addr, mnemonic) =
52 create_wallet(client.as_querier().chain_config.clone(), rng).await?;
53 log(WalletLog::Create { addr, mnemonic });
54 }
55 WalletCommand::Show => {
56 let balances = client
57 .as_querier()
58 .all_balances(client.as_signing().addr.clone(), None)
59 .await?;
60
61 if balances.is_empty() {
62 log(WalletLog::Show {
63 addr: client.as_signing().addr.clone(),
64 balances: vec![],
65 });
66 } else {
67 log(WalletLog::Show {
68 addr: client.as_signing().addr.clone(),
69 balances: balances.clone(),
70 });
71 }
72 }
73 WalletCommand::Balance { address, denom } => {
74 let addr = client.as_querier().chain_config.parse_address(address)?;
75 let balance = client
76 .as_querier()
77 .balance(addr.clone(), denom.clone())
78 .await?;
79 let denom = denom
80 .clone()
81 .unwrap_or_else(|| client.as_querier().chain_config.gas_denom.clone());
82 log(WalletLog::Balance {
83 addr,
84 balance: new_coin(balance.unwrap_or_default(), denom),
85 });
86 }
87 WalletCommand::AllBalances { address } => {
88 let addr = client.as_querier().chain_config.parse_address(address)?;
89 let balances = client.as_querier().all_balances(addr.clone(), None).await?;
90 log(WalletLog::AllBalances { addr, balances });
91 }
92 WalletCommand::Transfer { to, amount, denom } => {
93 let to = client.as_querier().chain_config.parse_address(to)?;
94 let tx_resp = client
95 .as_signing()
96 .transfer(*amount, &to, denom.as_deref(), None)
97 .await?;
98 log(WalletLog::Transfer {
99 to,
100 amount: *amount,
101 denom: denom
102 .clone()
103 .unwrap_or_else(|| client.as_querier().chain_config.gas_denom.clone()),
104 tx_resp: Box::new(tx_resp),
105 });
106 }
107 }
108 Ok(())
109 }
110}
111
112pub enum WalletLog {
113 Create {
114 addr: Address,
115 mnemonic: Mnemonic,
116 },
117 Show {
118 addr: Address,
119 balances: Vec<Coin>,
120 },
121 Balance {
122 addr: Address,
123 balance: Coin,
124 },
125 AllBalances {
126 addr: Address,
127 balances: Vec<Coin>,
128 },
129 Transfer {
130 to: Address,
131 amount: u128,
132 denom: String,
133 tx_resp: Box<TxResponse>,
134 },
135}
136
137pub async fn create_wallet(
138 chain_config: ChainConfig,
139 rng: &mut impl Rng,
140) -> Result<(Address, Mnemonic)> {
141 let entropy: [u8; 32] = rng.random();
142 let mnemonic = Mnemonic::from_entropy(&entropy)?;
143
144 let signer = KeySigner::new_mnemonic_iter(mnemonic.words(), None)?;
145 let addr = chain_config.address_from_pub_key(&signer.public_key().await?)?;
146
147 Ok((addr, mnemonic))
148}