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 {
14 #[arg(long)]
15 mnemonic: Option<String>,
16 },
17 Balance {
19 #[arg(long)]
20 address: String,
22 #[arg(long)]
24 denom: Option<String>,
25 },
26 AllBalances {
27 #[arg(long)]
28 address: String,
30 },
31 Transfer {
33 #[arg(long)]
34 to: String,
36 #[arg(long)]
38 amount: u128,
39 #[arg(long)]
41 denom: Option<String>,
42 },
43}
44
45impl WalletCommand {
46 pub async fn run(
47 &self,
48 client: impl Into<AnyClient>,
49 rng: &mut impl Rng,
50 log: impl Fn(WalletLog),
51 ) -> Result<()> {
52 let client = client.into();
53 match self {
54 WalletCommand::Create => {
55 let (addr, mnemonic) =
56 create_wallet(client.as_querier().chain_config.clone(), rng).await?;
57 log(WalletLog::Create { addr, mnemonic });
58 }
59 WalletCommand::Show { mnemonic } => {
60 let addr = match mnemonic {
61 None => client.as_signing().addr.clone(),
62 Some(mnemonic) => {
63 let signer = KeySigner::new_mnemonic_str(mnemonic, None)?;
64 client
65 .as_querier()
66 .chain_config
67 .address_from_pub_key(&signer.public_key().await?)?
68 }
69 };
70
71 let balances = client.as_querier().all_balances(addr.clone(), None).await?;
72
73 if balances.is_empty() {
74 log(WalletLog::Show {
75 addr,
76 balances: vec![],
77 });
78 } else {
79 log(WalletLog::Show {
80 addr,
81 balances: balances.clone(),
82 });
83 }
84 }
85 WalletCommand::Balance { address, denom } => {
86 let addr = client.as_querier().chain_config.parse_address(address)?;
87 let balance = client
88 .as_querier()
89 .balance(addr.clone(), denom.clone())
90 .await?;
91 let denom = denom
92 .clone()
93 .unwrap_or_else(|| client.as_querier().chain_config.gas_denom.clone());
94 log(WalletLog::Balance {
95 addr,
96 balance: new_coin(balance.unwrap_or_default(), denom),
97 });
98 }
99 WalletCommand::AllBalances { address } => {
100 let addr = client.as_querier().chain_config.parse_address(address)?;
101 let balances = client.as_querier().all_balances(addr.clone(), None).await?;
102 log(WalletLog::AllBalances { addr, balances });
103 }
104 WalletCommand::Transfer { to, amount, denom } => {
105 let to = client.as_querier().chain_config.parse_address(to)?;
106 let tx_resp = client
107 .as_signing()
108 .transfer(*amount, &to, denom.as_deref(), None)
109 .await?;
110 log(WalletLog::Transfer {
111 to,
112 amount: *amount,
113 denom: denom
114 .clone()
115 .unwrap_or_else(|| client.as_querier().chain_config.gas_denom.clone()),
116 tx_resp: Box::new(tx_resp),
117 });
118 }
119 }
120 Ok(())
121 }
122}
123
124pub enum WalletLog {
125 Create {
126 addr: Address,
127 mnemonic: Mnemonic,
128 },
129 Show {
130 addr: Address,
131 balances: Vec<Coin>,
132 },
133 Balance {
134 addr: Address,
135 balance: Coin,
136 },
137 AllBalances {
138 addr: Address,
139 balances: Vec<Coin>,
140 },
141 Transfer {
142 to: Address,
143 amount: u128,
144 denom: String,
145 tx_resp: Box<TxResponse>,
146 },
147}
148
149pub async fn create_wallet(
150 chain_config: ChainConfig,
151 rng: &mut impl Rng,
152) -> Result<(Address, Mnemonic)> {
153 let entropy: [u8; 32] = rng.random();
154 let mnemonic = Mnemonic::from_entropy(&entropy)?;
155
156 let signer = KeySigner::new_mnemonic_iter(mnemonic.words(), None)?;
157 let addr = chain_config.address_from_pub_key(&signer.public_key().await?)?;
158
159 Ok((addr, mnemonic))
160}