neo3/neo_contract/traits/
fungible_token.rs

1use crate::{
2	builder::{AccountSigner, TransactionBuilder},
3	neo_clients::JsonRpcProvider,
4	neo_contract::{ContractError, FungibleTokenContract, TokenTrait},
5	neo_protocol::{Account, AccountTrait},
6	neo_wallets::Wallet,
7	Bytes, ContractParameter, NNSName, ScriptHash,
8};
9use async_trait::async_trait;
10use primitive_types::H160;
11
12#[async_trait]
13pub trait FungibleTokenTrait<'a, P: JsonRpcProvider>: TokenTrait<'a, P> {
14	const BALANCE_OF: &'static str = "balanceOf";
15	const TRANSFER: &'static str = "transfer";
16
17	async fn get_balance_of(&self, script_hash: &ScriptHash) -> Result<i32, ContractError> {
18		self.get_balance_of_hash160(script_hash).await
19	}
20
21	async fn get_balance_of_hash160(&self, script_hash: &H160) -> Result<i32, ContractError> {
22		self.call_function_returning_int(Self::BALANCE_OF, vec![script_hash.into()])
23			.await
24	}
25
26	async fn get_total_balance(&self, wallet: &Wallet) -> Result<i32, ContractError> {
27		let mut sum = 0;
28		for (_, account) in &wallet.accounts {
29			sum += self
30				.get_balance_of(&account.address_or_scripthash().script_hash())
31				.await
32				.unwrap();
33		}
34		Ok(sum)
35	}
36
37	async fn transfer_from_account(
38		&self,
39		from: &Account,
40		to: &ScriptHash,
41		amount: i32,
42		data: Option<ContractParameter>,
43	) -> Result<TransactionBuilder<P>, ContractError> {
44		let mut builder = self
45			.transfer_from_hash160(&from.address_or_scripthash().script_hash(), to, amount, data)
46			.await?;
47		builder.set_signers(vec![AccountSigner::called_by_entry(from).unwrap().into()]);
48
49		Ok(builder)
50	}
51
52	async fn transfer_from_hash160(
53		&self,
54		from: &ScriptHash,
55		to: &ScriptHash,
56		amount: i32,
57		data: Option<ContractParameter>,
58	) -> Result<TransactionBuilder<P>, ContractError> {
59		if amount < 0 {
60			return Err(ContractError::InvalidArgError(
61				"The amount must be greater than or equal to 0.".to_string(),
62			));
63		}
64
65		let transfer_script = self.build_transfer_script(from, to, amount, data).await.unwrap();
66		let mut builder = TransactionBuilder::new();
67		builder.set_script(Some(transfer_script));
68		Ok(builder)
69	}
70
71	async fn build_transfer_script(
72		&self,
73		from: &ScriptHash,
74		to: &ScriptHash,
75		amount: i32,
76		data: Option<ContractParameter>,
77	) -> Result<Bytes, ContractError> {
78		self.build_invoke_function_script(
79			<FungibleTokenContract<P> as FungibleTokenTrait<P>>::TRANSFER,
80			vec![from.into(), to.into(), amount.into(), data.unwrap()],
81		)
82		.await
83	}
84
85	// MARK: Transfer using NNS
86
87	async fn transfer_from_account_to_nns(
88		&self,
89		from: &Account,
90		to: &NNSName,
91		amount: i32,
92		data: Option<ContractParameter>,
93	) -> Result<TransactionBuilder<P>, ContractError> {
94		let mut builder = self
95			.transfer_from_hash160_to_nns(&from.get_script_hash(), to, amount, data)
96			.await
97			.unwrap();
98		builder.set_signers(vec![AccountSigner::called_by_entry(from).unwrap().into()]);
99
100		Ok(builder)
101	}
102
103	async fn transfer_from_hash160_to_nns(
104		&self,
105		from: &ScriptHash,
106		to: &NNSName,
107		amount: i32,
108		data: Option<ContractParameter>,
109	) -> Result<TransactionBuilder<P>, ContractError> {
110		let script_hash = self.resolve_nns_text_record(to).await.unwrap();
111		self.transfer_from_hash160(from, &script_hash, amount, data).await
112	}
113}