1use autonomi::{
2 client::payment::PaymentOption, Bytes, Chunk, ChunkAddress, Client, GraphEntry,
3 GraphEntryAddress, PublicKey, SecretKey, XorName,
4};
5use futures::{future::Future, stream, StreamExt};
6use ruint::aliases::U256;
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Debug, Serialize, Deserialize)]
10pub struct TokenInfo {
11 pub symbol: String,
12 pub name: String,
13 pub decimals: u8,
14}
15
16pub trait ActExt {
17 fn act_create(
18 &self,
19 name: String,
20 symbol: String,
21 decimals: u8,
22 total_supply: U256,
23 issuer_key: PublicKey,
24 payment: &PaymentOption,
25 ) -> impl Future<Output = Result<(PublicKey, XorName), String>> + Send;
26
27 fn act_unspent(
28 &self,
29 pubkey: &PublicKey,
30 spend: PublicKey,
31 ) -> impl Future<Output = Result<(XorName, U256), String>> + Send;
32
33 fn act_balance(
34 &self,
35 pubkey: &PublicKey,
36 spends: Vec<PublicKey>,
37 ) -> impl Future<Output = Result<U256, String>> + Send;
38
39 fn act_token_info(
40 &self,
41 token_id: &XorName,
42 ) -> impl Future<Output = Result<TokenInfo, String>> + Send;
43}
44
45impl ActExt for Client {
46 async fn act_create(
47 &self,
48 name: String,
49 symbol: String,
50 decimals: u8,
51 total_supply: U256,
52 to: PublicKey,
53 payment: &PaymentOption,
54 ) -> Result<(PublicKey, XorName), String> {
55 let token_info_bytes = Bytes::from(
57 serde_json::to_string(&TokenInfo {
58 name,
59 symbol,
60 decimals,
61 })
62 .map_err(|e| format!("{}", e))?,
63 );
64
65 let token_info = Chunk::new(token_info_bytes.clone());
66 let (_paid, token_info_address) = self
67 .chunk_put(&token_info, payment.clone())
68 .await
69 .map_err(|e| format!("{}", e))?;
70
71 println!("TokenInfo Chunk: {}", token_info_address);
72
73 let token_id = token_info_address.xorname();
74 println!("TokenId: {}", token_id);
75
76 let genesis_owner = SecretKey::random();
77 println!("Genesis owner: {:?}", genesis_owner);
78
79 let genesis_owner_pubkey = genesis_owner.public_key();
80 let genesis = GraphEntry::new(
81 &genesis_owner,
82 vec![],
83 token_id.0.clone(),
84 vec![(to, total_supply.to_be_bytes())], );
86 let (_paid, genesis_address) = self
87 .graph_entry_put(genesis, payment.clone())
88 .await
89 .map_err(|e| format!("{}", e))?;
90
91 println!("Genesis GraphEntry: {}", genesis_address);
92 let genesis_spend = *genesis_address.owner();
93
94 assert_eq!(genesis_owner_pubkey, genesis_spend);
95
96 Ok((genesis_spend, *token_id))
97 }
98
99 async fn act_unspent(
100 &self,
101 output: &PublicKey,
102 spend: PublicKey,
103 ) -> Result<(XorName, U256), String> {
104 let tx = self
105 .graph_entry_get(&GraphEntryAddress::new(spend))
106 .await
107 .map_err(|e| format!("{}", e))?;
108
109 let (balance, overflow) = tx
110 .descendants
111 .iter()
112 .filter(|(pk, _data)| pk == output)
113 .map(|(_pk, data)| U256::from_be_bytes(*data))
114 .fold((U256::ZERO, false), |(sum, any_overflow), n| {
115 let (added, this_overflow) = sum.overflowing_add(n);
116 (added, any_overflow || this_overflow)
117 });
118
119 match overflow {
120 false => Ok((XorName(tx.content), balance)),
121 true => Err("Overflow.".into()),
122 }
123 }
124
125 async fn act_balance(
126 &self,
127 pubkey: &PublicKey,
128 spends: Vec<PublicKey>,
129 ) -> Result<U256, String> {
130 let stream = stream::iter(spends);
131
132 stream
133 .fold(Ok(U256::ZERO), |sum_res, spend_pk| async move {
134 let (_token_id, unsp) = self.act_unspent(pubkey, spend_pk).await?;
135
136 match sum_res?.overflowing_add(unsp) {
137 (added, false) => Ok(added),
138 (_, true) => Err("Overflow.".into()),
139 }
140 })
141 .await
142 }
143
144 async fn act_token_info(&self, token_id: &XorName) -> Result<TokenInfo, String> {
145 let token_info_address = ChunkAddress::new(*token_id);
146
147 let chunk = self
148 .chunk_get(&token_info_address)
149 .await
150 .map_err(|e| format!("{}", e))?;
151
152 let token_info: TokenInfo =
153 serde_json::from_slice(chunk.value()).map_err(|e| format!("{}", e))?;
154
155 Ok(token_info)
156 }
157
158 }