neo3/neo_contract/traits/
token.rs1use async_trait::async_trait;
2use num_traits::{real::Real, ToPrimitive};
3use primitive_types::H160;
4use rust_decimal::Decimal;
5
6use crate::{
7 neo_clients::JsonRpcProvider,
8 neo_contract::{ContractError, SmartContractTrait},
9 neo_types::NNSName,
10};
11
12#[async_trait]
13pub trait TokenTrait<'a, P: JsonRpcProvider>: SmartContractTrait<'a, P = P> {
14 const TOTAL_SUPPLY: &'static str = "totalSupply";
15 const SYMBOL: &'static str = "symbol";
16 const DECIMALS: &'static str = "decimals";
17
18 fn total_supply(&self) -> Option<u64>;
19
20 fn set_total_supply(&mut self, total_supply: u64);
21
22 fn decimals(&self) -> Option<u8>;
23
24 fn set_decimals(&mut self, decimals: u8);
25
26 fn symbol(&self) -> Option<String>;
27
28 fn set_symbol(&mut self, symbol: String);
29
30 async fn get_total_supply(&mut self) -> Result<u64, ContractError> {
31 if let Some(supply) = &self.total_supply() {
32 return Ok(supply.clone().into());
33 }
34
35 let supply =
36 self.call_function_returning_int(Self::TOTAL_SUPPLY, vec![]).await.unwrap() as u64;
37
38 self.set_total_supply(supply);
39 Ok(supply)
40 }
41
42 async fn get_decimals(&mut self) -> Result<u8, ContractError> {
43 if let Some(decimals) = &self.decimals() {
44 return Ok(decimals.clone().into());
45 }
46
47 let decimals =
48 self.call_function_returning_int(Self::DECIMALS, vec![]).await.unwrap() as u8;
49
50 self.set_decimals(decimals);
51 Ok(decimals)
52 }
53
54 async fn get_symbol(&mut self) -> Result<String, ContractError> {
57 if let Some(symbol) = &self.symbol() {
58 return Ok(symbol.clone());
59 }
60
61 let symbol = self.call_function_returning_string(Self::SYMBOL, vec![]).await.unwrap();
62
63 self.set_symbol(symbol.clone());
64 Ok(symbol)
65 }
66
67 fn to_fractions(&self, amount: u64, decimals: u32) -> Result<i32, ContractError> {
68 let scale = (amount as f64).log10().floor() as u32 + 1;
69 if scale > decimals {
70 return Err(ContractError::RuntimeError(
71 "Amount has too many decimal points".to_string(),
72 ));
73 }
74
75 let scaled = Decimal::from(amount) * Decimal::from(10i32.pow(decimals));
76 Ok(scaled.trunc().to_i32().unwrap())
77 }
78
79 fn to_fractions_decimal(&self, amount: Decimal, decimals: u32) -> Result<u64, ContractError> {
80 if amount.scale() > decimals {
81 return Err(ContractError::RuntimeError(
82 "Amount has too many decimal places".to_string(),
83 ));
84 }
85
86 let mut scaled = amount;
87 scaled *= Decimal::from(10_u32.pow(decimals));
88
89 let fractions = scaled.trunc().to_u64().unwrap();
90 Ok(fractions)
91 }
92
93 fn to_decimals_u64(&self, fractions: u64, decimals: u32) -> Decimal {
94 let divisor = Decimal::from(10_u32.pow(decimals));
95 let amount = Decimal::from(fractions);
96
97 amount / divisor
98 }
99
100 fn to_decimals(&self, amount: i64, decimals: u32) -> Decimal {
101 let divisor = Decimal::from(10_u32.pow(decimals));
102 let decimal_amount = Decimal::from(amount);
103
104 decimal_amount / divisor
106 }
107
108 async fn resolve_nns_text_record(&self, name: &NNSName) -> Result<H160, ContractError>;
109 }