use async_trait::async_trait;
use num_traits::ToPrimitive;
use primitive_types::H160;
use rust_decimal::Decimal;
use crate::{
neo_clients::JsonRpcProvider,
neo_contract::{ContractError, SmartContractTrait},
neo_types::NNSName,
};
#[async_trait]
pub trait TokenTrait<'a, P: JsonRpcProvider>: SmartContractTrait<'a, P = P> {
const TOTAL_SUPPLY: &'static str = "totalSupply";
const SYMBOL: &'static str = "symbol";
const DECIMALS: &'static str = "decimals";
fn total_supply(&self) -> Option<u64>;
fn set_total_supply(&mut self, total_supply: u64);
fn decimals(&self) -> Option<u8>;
fn set_decimals(&mut self, decimals: u8);
fn symbol(&self) -> Option<String>;
fn set_symbol(&mut self, symbol: String);
async fn get_total_supply(&mut self) -> Result<u64, ContractError> {
if let Some(supply) = &self.total_supply() {
return Ok(*supply);
}
let supply = self.call_function_returning_int(Self::TOTAL_SUPPLY, vec![]).await? as u64;
self.set_total_supply(supply);
Ok(supply)
}
async fn get_decimals(&mut self) -> Result<u8, ContractError> {
if let Some(decimals) = &self.decimals() {
return Ok(*decimals);
}
let decimals = self.call_function_returning_int(Self::DECIMALS, vec![]).await? as u8;
self.set_decimals(decimals);
Ok(decimals)
}
async fn get_symbol(&mut self) -> Result<String, ContractError> {
if let Some(symbol) = &self.symbol() {
return Ok(symbol.clone());
}
let symbol = self.call_function_returning_string(Self::SYMBOL, vec![]).await?;
self.set_symbol(symbol.clone());
Ok(symbol)
}
fn to_fractions(&self, amount: u64, decimals: u32) -> Result<i64, ContractError> {
let scaled = Decimal::from(amount) * Decimal::from(10i32.pow(decimals));
scaled.trunc().to_i64().ok_or_else(|| {
ContractError::RuntimeError("Amount is too large to fit into i64 fractions".to_string())
})
}
fn to_fractions_decimal(&self, amount: Decimal, decimals: u32) -> Result<u64, ContractError> {
if amount.scale() > decimals {
return Err(ContractError::RuntimeError(
"Amount has too many decimal places".to_string(),
));
}
let mut scaled = amount;
scaled *= Decimal::from(10_u32.pow(decimals));
scaled.trunc().to_u64().ok_or_else(|| {
ContractError::RuntimeError("Amount is too large to fit into u64 fractions".to_string())
})
}
fn to_decimals_u64(&self, fractions: u64, decimals: u32) -> Decimal {
let divisor = Decimal::from(10_u32.pow(decimals));
let amount = Decimal::from(fractions);
amount / divisor
}
fn to_decimals(&self, amount: i64, decimals: u32) -> Decimal {
let divisor = Decimal::from(10_u32.pow(decimals));
let decimal_amount = Decimal::from(amount);
decimal_amount / divisor
}
async fn resolve_nns_text_record(&self, name: &NNSName) -> Result<H160, ContractError>;
}