Skip to main content

aelf_sdk/
lib.rs

1//! High-level facade for the AElf Rust SDK.
2//!
3//! This crate re-exports the most commonly used client, wallet, keystore and
4//! contract APIs behind a single dependency.
5
6#![forbid(unsafe_code)]
7
8pub use aelf_client::config::{BasicAuth, ClientConfig, RetryPolicy};
9pub use aelf_client::dto;
10#[cfg(feature = "native-http")]
11pub use aelf_client::provider::HttpProvider;
12pub use aelf_client::provider::Provider;
13pub use aelf_client::{AElfError, KeyPairInfo, TransactionBuilder};
14pub use aelf_contract::{
15    AedposContract, ContractError, CrossChainContract, DynamicContract, ElectionContract,
16    TokenContract, VoteContract, ZeroContract,
17};
18pub use aelf_crypto::{
19    address_from_public_key, address_to_pb, base58_to_chain_id, chain_id_to_base58, decode_address,
20    hash_to_pb, parse_aelf_address, pb_to_address, sign_transaction,
21    sign_transaction_with_private_key, transaction_hash, CryptoError, Wallet, DEFAULT_BIP44_PATH,
22};
23pub use aelf_keystore::{Keystore, KeystoreEncryptOptions, KeystoreError, UnlockedKeystore};
24pub use aelf_proto as proto;
25
26/// High-level async client that mirrors the main entry point of the other AElf SDKs.
27#[derive(Clone)]
28pub struct AElfClient {
29    inner: aelf_client::AElfClient,
30}
31
32impl AElfClient {
33    /// Creates a facade client backed by the default HTTP provider.
34    #[cfg(feature = "native-http")]
35    pub fn new(config: ClientConfig) -> Result<Self, AElfError> {
36        Ok(Self {
37            inner: aelf_client::AElfClient::new(config)?,
38        })
39    }
40
41    /// Creates a facade client from a custom provider implementation.
42    pub fn with_provider<P>(provider: P) -> Result<Self, AElfError>
43    where
44        P: Provider + 'static,
45    {
46        Ok(Self {
47            inner: aelf_client::AElfClient::with_provider(provider)?,
48        })
49    }
50
51    /// Returns block-related APIs.
52    pub fn block(&self) -> aelf_client::BlockService {
53        self.inner.block()
54    }
55
56    /// Returns chain-related APIs.
57    pub fn chain(&self) -> aelf_client::ChainService {
58        self.inner.chain()
59    }
60
61    /// Returns network-related APIs.
62    pub fn net(&self) -> aelf_client::NetService {
63        self.inner.net()
64    }
65
66    /// Returns transaction-related APIs.
67    pub fn tx(&self) -> aelf_client::TransactionService {
68        self.inner.tx()
69    }
70
71    /// Returns local utility helpers.
72    pub fn utils(&self) -> aelf_client::ClientUtilsService {
73        self.inner.utils()
74    }
75
76    /// Creates a transaction builder bound to this client.
77    pub fn transaction_builder(&self) -> TransactionBuilder {
78        self.inner.transaction_builder()
79    }
80
81    /// Loads a contract descriptor from chain and returns a dynamic contract handle.
82    pub async fn contract_at(
83        &self,
84        address: impl Into<String>,
85        wallet: Wallet,
86    ) -> Result<DynamicContract, ContractError> {
87        DynamicContract::at(self.inner.clone(), address.into(), wallet).await
88    }
89
90    /// Returns a typed wrapper for the genesis zero contract.
91    pub fn zero_contract(&self, address: impl Into<String>, wallet: Wallet) -> ZeroContract {
92        ZeroContract::new(self.inner.clone(), wallet, address)
93    }
94
95    /// Returns a typed wrapper for the token contract.
96    pub fn token_contract(&self, address: impl Into<String>, wallet: Wallet) -> TokenContract {
97        TokenContract::new(self.inner.clone(), wallet, address)
98    }
99
100    /// Returns a typed wrapper for the election contract.
101    pub fn election_contract(
102        &self,
103        address: impl Into<String>,
104        wallet: Wallet,
105    ) -> ElectionContract {
106        ElectionContract::new(self.inner.clone(), wallet, address)
107    }
108
109    /// Returns a typed wrapper for the vote contract.
110    pub fn vote_contract(&self, address: impl Into<String>, wallet: Wallet) -> VoteContract {
111        VoteContract::new(self.inner.clone(), wallet, address)
112    }
113
114    /// Returns a typed wrapper for the cross-chain contract.
115    pub fn cross_chain_contract(
116        &self,
117        address: impl Into<String>,
118        wallet: Wallet,
119    ) -> CrossChainContract {
120        CrossChainContract::new(self.inner.clone(), wallet, address)
121    }
122
123    /// Returns a typed wrapper for the AEDPoS contract.
124    pub fn aedpos_contract(&self, address: impl Into<String>, wallet: Wallet) -> AedposContract {
125        AedposContract::new(self.inner.clone(), wallet, address)
126    }
127
128    /// Exposes the lower-level client for advanced use cases.
129    pub fn inner(&self) -> &aelf_client::AElfClient {
130        &self.inner
131    }
132}
133
134/// Formats a raw token amount using the token's decimals.
135pub fn format_token_amount(amount: i64, decimals: i32) -> String {
136    if decimals <= 0 {
137        return amount.to_string();
138    }
139
140    let negative = amount.is_negative();
141    let digits = amount.unsigned_abs().to_string();
142    let scale = decimals as usize;
143    let formatted = if digits.len() <= scale {
144        let mut value = String::from("0.");
145        value.push_str(&"0".repeat(scale - digits.len()));
146        value.push_str(&digits);
147        value
148    } else {
149        let split = digits.len() - scale;
150        format!("{}.{}", &digits[..split], &digits[split..])
151    };
152
153    if negative {
154        format!("-{formatted}")
155    } else {
156        formatted
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::format_token_amount;
163
164    #[test]
165    fn formats_token_amount_with_decimals() {
166        assert_eq!(format_token_amount(123_456_789, 8), "1.23456789");
167        assert_eq!(format_token_amount(1, 8), "0.00000001");
168        assert_eq!(format_token_amount(-5, 2), "-0.05");
169    }
170}