1#![deny(missing_docs)]
3#![deny(warnings)]
4use crate::types::Amount;
5use anyhow::{Context, Result};
6use fraction::{BigDecimal, BigInt};
7use std::path::Path;
8
9pub use crate::client::Client;
10pub use crate::mnemonic::{generate_mnemonic, MnemonicStore};
11pub use crate::signer::{RosettaAccount, RosettaPublicKey, Signer};
12pub use crate::wallet::EthereumExt;
13pub use crate::wallet::Wallet;
14pub use rosetta_core::{crypto, types, BlockchainConfig, TransactionBuilder};
15
16mod client;
17mod mnemonic;
18mod signer;
19mod wallet;
20
21pub fn amount_to_string(amount: &Amount) -> Result<String> {
23 let value = BigInt::parse_bytes(amount.value.as_bytes(), 10)
24 .ok_or_else(|| anyhow::anyhow!("invalid amount {:?}", amount))?;
25 let decimals = BigInt::pow(&10u32.into(), amount.currency.decimals);
26 let value = BigDecimal::from(value) / BigDecimal::from(decimals);
27 Ok(format!("{:.256} {}", value, amount.currency.symbol))
28}
29
30pub fn string_to_amount(amount: &str, decimals: u32) -> Result<u128> {
35 let (amount, decimals): (u128, u32) = if let Some((main, rest)) = amount.split_once('.') {
36 let decimals = decimals
37 .checked_sub(rest.chars().count() as _)
38 .context("too many decimals")?;
39 let mut amount = main.to_string();
40 amount.push_str(rest);
41 (amount.parse()?, decimals)
42 } else {
43 (amount.parse()?, decimals)
44 };
45 amount
46 .checked_mul(u128::pow(10, decimals))
47 .context("u128 overflow")
48}
49
50pub fn create_config(blockchain: &str, network: &str) -> Result<BlockchainConfig> {
52 match blockchain {
53 "bitcoin" => rosetta_config_bitcoin::config(network),
54 "ethereum" => rosetta_config_ethereum::config(network),
55 "astar" => rosetta_config_astar::config(network),
56 "polkadot" => rosetta_config_polkadot::config(network),
57 _ => anyhow::bail!("unsupported blockchain"),
58 }
59}
60
61pub fn create_signer(_keyfile: Option<&Path>) -> Result<Signer> {
63 let store = MnemonicStore::new(_keyfile)?;
64 let mnemonic = store.get_or_generate_mnemonic()?;
65 Signer::new(&mnemonic, "")
66}
67
68pub async fn create_client(
74 blockchain: Option<String>,
75 network: Option<String>,
76 url: Option<String>,
77) -> Result<(BlockchainConfig, Client)> {
78 let (blockchain, network) = if let (Some(blockchain), Some(network)) = (blockchain, network) {
79 (blockchain, network)
80 } else if let Some(url) = url.as_ref() {
81 let network = Client::new(url)?.network_list().await?[0].clone();
82 (network.blockchain, network.network)
83 } else {
84 anyhow::bail!("requires url or blockchain argument");
85 };
86 let config = create_config(&blockchain, &network)?;
87 let url = url.unwrap_or_else(|| config.connector_url());
88 let client = Client::new(&url)?;
89 Ok((config, client))
90}
91
92pub async fn create_wallet(
99 blockchain: Option<String>,
100 network: Option<String>,
101 url: Option<String>,
102 keyfile: Option<&Path>,
103) -> Result<Wallet> {
104 let (config, client) = create_client(blockchain, network, url).await?;
105 let signer = create_signer(keyfile)?;
106 Wallet::new(config, &signer, client)
107}