use async_trait::async_trait;
use hex_literal::hex;
use primitive_types::H160;
use serde::{Deserialize, Serialize};
use crate::{
builder::{AccountSigner, TransactionBuilder},
neo_clients::{JsonRpcProvider, RpcClient},
neo_contract::{ContractError, SmartContractTrait, TokenTrait},
neo_protocol::Account,
};
use neo3::prelude::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NeoburgerContract<'a, P: JsonRpcProvider> {
#[serde(deserialize_with = "deserialize_script_hash")]
#[serde(serialize_with = "serialize_script_hash")]
script_hash: ScriptHash,
#[serde(skip_serializing_if = "Option::is_none")]
total_supply: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
decimals: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
symbol: Option<String>,
#[serde(skip)]
provider: Option<&'a RpcClient<P>>,
}
impl<'a, P: JsonRpcProvider + 'static> NeoburgerContract<'a, P> {
pub const CONTRACT_HASH: &'static str = "48c40d4666f93408be1bef038b6722404f5c4a5a";
pub const SYMBOL: &'static str = "bNEO";
pub const DECIMALS: u8 = 8;
pub const WRAP: &'static str = "wrap";
pub const UNWRAP: &'static str = "unwrap";
pub const CLAIM_GAS: &'static str = "claimGas";
pub const GET_RATE: &'static str = "getRate";
pub fn new(provider: Option<&'a RpcClient<P>>) -> Self {
Self {
script_hash: ScriptHash::from(hex!("48c40d4666f93408be1bef038b6722404f5c4a5a")),
total_supply: None,
decimals: Some(Self::DECIMALS),
symbol: Some(Self::SYMBOL.to_string()),
provider,
}
}
pub fn with_script_hash(script_hash: ScriptHash, provider: Option<&'a RpcClient<P>>) -> Self {
Self {
script_hash,
total_supply: None,
decimals: Some(Self::DECIMALS),
symbol: Some(Self::SYMBOL.to_string()),
provider,
}
}
pub async fn wrap(
&self,
amount: i64,
account: &Account,
) -> Result<TransactionBuilder<'_, P>, ContractError> {
let params = vec![ContractParameter::integer(amount)];
let mut builder = self.invoke_function(Self::WRAP, params).await?;
let signer = AccountSigner::called_by_entry(account)
.map_err(|err| ContractError::RuntimeError(err.to_string()))?;
builder
.set_signers(vec![signer.into()])
.map_err(|err| ContractError::RuntimeError(err.to_string()))?;
Ok(builder)
}
pub async fn unwrap(
&self,
amount: i64,
account: &Account,
) -> Result<TransactionBuilder<'_, P>, ContractError> {
let params = vec![ContractParameter::integer(amount)];
let mut builder = self.invoke_function(Self::UNWRAP, params).await?;
let signer = AccountSigner::called_by_entry(account)
.map_err(|err| ContractError::RuntimeError(err.to_string()))?;
builder
.set_signers(vec![signer.into()])
.map_err(|err| ContractError::RuntimeError(err.to_string()))?;
Ok(builder)
}
pub async fn claim_gas(
&self,
account: &Account,
) -> Result<TransactionBuilder<'_, P>, ContractError> {
let params = vec![];
let mut builder = self.invoke_function(Self::CLAIM_GAS, params).await?;
let signer = AccountSigner::called_by_entry(account)
.map_err(|err| ContractError::RuntimeError(err.to_string()))?;
builder
.set_signers(vec![signer.into()])
.map_err(|err| ContractError::RuntimeError(err.to_string()))?;
Ok(builder)
}
pub async fn get_rate(&self) -> Result<f64, ContractError> {
let result = self.call_function_returning_int(Self::GET_RATE, vec![]).await?;
Ok(result as f64 / 100_000_000.0) }
}
#[async_trait]
impl<'a, P: JsonRpcProvider> SmartContractTrait<'a> for NeoburgerContract<'a, P> {
type P = P;
fn script_hash(&self) -> H160 {
self.script_hash
}
fn set_script_hash(&mut self, script_hash: H160) {
self.script_hash = script_hash;
}
fn provider(&self) -> Option<&RpcClient<P>> {
self.provider
}
}
#[async_trait]
impl<'a, P: JsonRpcProvider> TokenTrait<'a, P> for NeoburgerContract<'a, P> {
fn total_supply(&self) -> Option<u64> {
self.total_supply
}
fn set_total_supply(&mut self, total_supply: u64) {
self.total_supply = Some(total_supply);
}
fn decimals(&self) -> Option<u8> {
self.decimals
}
fn set_decimals(&mut self, decimals: u8) {
self.decimals = Some(decimals);
}
fn symbol(&self) -> Option<String> {
self.symbol.clone()
}
fn set_symbol(&mut self, symbol: String) {
self.symbol = Some(symbol);
}
async fn resolve_nns_text_record(&self, _name: &NNSName) -> Result<H160, ContractError> {
Err(ContractError::InvalidNeoName(
"NeoburgerNeo does not support NNS resolution".to_string(),
))
}
}