neo3/neo_contract/traits/
token.rs

1use 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	// Other methods
55
56	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		// u32 is always non-negative, so this check is redundant
105		decimal_amount / divisor
106	}
107
108	async fn resolve_nns_text_record(&self, name: &NNSName) -> Result<H160, ContractError>;
109	// {
110	// 	let req = {
111	// 		self.provider()
112	// 			.unwrap()
113	// 			.invoke_function(
114	// 				&NeoNameService::new().script_hash(),
115	// 				"resolve".to_string(),
116	// 				vec![
117	// 					ContractParameter::from(name.name()),
118	// 					ContractParameter::from(RecordType::TXT.byte_repr()),
119	// 				],
120	// 				(),
121	// 			)
122	// 			.clone()
123	// 	};
124	//
125	// 	let address = req.await.unwrap().stack.first().unwrap().clone();
126	//
127	//
128	// 	Ok(H160::from_slice(&address.as_bytes().unwrap()))
129	// }
130}