1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use std::convert::TryInto;
4
5use crate::msg::{AllowanceResponse, BalanceResponse, HandleMsg, InitMsg, QueryMsg};
6use cosmwasm::errors::{contract_err, dyn_contract_err, Result};
7use cosmwasm::traits::{Api, Extern, ReadonlyStorage, Storage};
8use cosmwasm::types::{log, CanonicalAddr, Env, HumanAddr, Response};
9use cw_storage::{serialize, PrefixedStorage, ReadonlyPrefixedStorage};
10
11#[derive(Serialize, Debug, Deserialize, Clone, PartialEq, JsonSchema)]
12pub struct Constants {
13 pub name: String,
14 pub symbol: String,
15 pub decimals: u8,
16}
17
18pub const PREFIX_CONFIG: &[u8] = b"config";
19pub const PREFIX_BALANCES: &[u8] = b"balances";
20pub const PREFIX_ALLOWANCES: &[u8] = b"allowances";
21
22pub const KEY_CONSTANTS: &[u8] = b"constants";
23pub const KEY_TOTAL_SUPPLY: &[u8] = b"total_supply";
24
25pub fn init<S: Storage, A: Api>(
26 deps: &mut Extern<S, A>,
27 _env: Env,
28 msg: InitMsg,
29) -> Result<Response> {
30 let mut total_supply: u128 = 0;
31 {
32 let mut balances_store = PrefixedStorage::new(PREFIX_BALANCES, &mut deps.storage);
34 for row in msg.initial_balances {
35 let raw_address = deps.api.canonical_address(&row.address)?;
36 let amount_raw = parse_u128(&row.amount)?;
37 balances_store.set(raw_address.as_slice(), &amount_raw.to_be_bytes());
38 total_supply += amount_raw;
39 }
40 }
41
42 if !is_valid_name(&msg.name) {
44 return contract_err("Name is not in the expected format (3-30 UTF-8 bytes)");
45 }
46 if !is_valid_symbol(&msg.symbol) {
47 return contract_err("Ticker symbol is not in expected format [A-Z]{3,6}");
48 }
49 if msg.decimals > 18 {
50 return contract_err("Decimals must not exceed 18");
51 }
52
53 let mut config_store = PrefixedStorage::new(PREFIX_CONFIG, &mut deps.storage);
54 let constants = serialize(&Constants {
55 name: msg.name,
56 symbol: msg.symbol,
57 decimals: msg.decimals,
58 })?;
59 config_store.set(KEY_CONSTANTS, &constants);
60 config_store.set(KEY_TOTAL_SUPPLY, &total_supply.to_be_bytes());
61
62 Ok(Response::default())
63}
64
65pub fn handle<S: Storage, A: Api>(
66 deps: &mut Extern<S, A>,
67 env: Env,
68 msg: HandleMsg,
69) -> Result<Response> {
70 match msg {
71 HandleMsg::Approve { spender, amount } => try_approve(deps, env, &spender, &amount),
72 HandleMsg::Transfer { recipient, amount } => try_transfer(deps, env, &recipient, &amount),
73 HandleMsg::TransferFrom {
74 owner,
75 recipient,
76 amount,
77 } => try_transfer_from(deps, env, &owner, &recipient, &amount),
78 }
79}
80
81pub fn query<S: Storage, A: Api>(deps: &Extern<S, A>, msg: QueryMsg) -> Result<Vec<u8>> {
82 match msg {
83 QueryMsg::Balance { address } => {
84 let address_key = deps.api.canonical_address(&address)?;
85 let balance = read_balance(&deps.storage, &address_key)?;
86 let out = serialize(&BalanceResponse {
87 balance: balance.to_string(),
88 })?;
89 Ok(out)
90 }
91 QueryMsg::Allowance { owner, spender } => {
92 let owner_key = deps.api.canonical_address(&owner)?;
93 let spender_key = deps.api.canonical_address(&spender)?;
94 let allowance = read_allowance(&deps.storage, &owner_key, &spender_key)?;
95 let out = serialize(&AllowanceResponse {
96 allowance: allowance.to_string(),
97 })?;
98 Ok(out)
99 }
100 }
101}
102
103fn try_transfer<S: Storage, A: Api>(
104 deps: &mut Extern<S, A>,
105 env: Env,
106 recipient: &HumanAddr,
107 amount: &str,
108) -> Result<Response> {
109 let sender_address_raw = &env.message.signer;
110 let recipient_address_raw = deps.api.canonical_address(recipient)?;
111 let amount_raw = parse_u128(amount)?;
112
113 perform_transfer(
114 &mut deps.storage,
115 &sender_address_raw,
116 &recipient_address_raw,
117 amount_raw,
118 )?;
119
120 let res = Response {
121 messages: vec![],
122 log: vec![
123 log("action", "transfer"),
124 log(
125 "sender",
126 deps.api.human_address(&env.message.signer)?.as_str(),
127 ),
128 log("recipient", recipient.as_str()),
129 ],
130 data: None,
131 };
132 Ok(res)
133}
134
135fn try_transfer_from<S: Storage, A: Api>(
136 deps: &mut Extern<S, A>,
137 env: Env,
138 owner: &HumanAddr,
139 recipient: &HumanAddr,
140 amount: &str,
141) -> Result<Response> {
142 let spender_address_raw = &env.message.signer;
143 let owner_address_raw = deps.api.canonical_address(owner)?;
144 let recipient_address_raw = deps.api.canonical_address(recipient)?;
145 let amount_raw = parse_u128(amount)?;
146
147 let mut allowance = read_allowance(&deps.storage, &owner_address_raw, &spender_address_raw)?;
148 if allowance < amount_raw {
149 return dyn_contract_err(format!(
150 "Insufficient allowance: allowance={}, required={}",
151 allowance, amount_raw
152 ));
153 }
154 allowance -= amount_raw;
155 write_allowance(
156 &mut deps.storage,
157 &owner_address_raw,
158 &spender_address_raw,
159 allowance,
160 );
161 perform_transfer(
162 &mut deps.storage,
163 &owner_address_raw,
164 &recipient_address_raw,
165 amount_raw,
166 )?;
167
168 let res = Response {
169 messages: vec![],
170 log: vec![
171 log("action", "transfer_from"),
172 log(
173 "spender",
174 deps.api.human_address(&env.message.signer)?.as_str(),
175 ),
176 log("sender", owner.as_str()),
177 log("recipient", recipient.as_str()),
178 ],
179 data: None,
180 };
181 Ok(res)
182}
183
184fn try_approve<S: Storage, A: Api>(
185 deps: &mut Extern<S, A>,
186 env: Env,
187 spender: &HumanAddr,
188 amount: &str,
189) -> Result<Response> {
190 let owner_address_raw = &env.message.signer;
191 let spender_address_raw = deps.api.canonical_address(spender)?;
192 let amount_raw = parse_u128(amount)?;
193 write_allowance(
194 &mut deps.storage,
195 &owner_address_raw,
196 &spender_address_raw,
197 amount_raw,
198 );
199 let res = Response {
200 messages: vec![],
201 log: vec![
202 log("action", "approve"),
203 log(
204 "owner",
205 deps.api.human_address(&env.message.signer)?.as_str(),
206 ),
207 log("spender", spender.as_str()),
208 ],
209 data: None,
210 };
211 Ok(res)
212}
213
214fn perform_transfer<T: Storage>(
215 store: &mut T,
216 from: &CanonicalAddr,
217 to: &CanonicalAddr,
218 amount: u128,
219) -> Result<()> {
220 let mut balances_store = PrefixedStorage::new(PREFIX_BALANCES, store);
221
222 let mut from_balance = read_u128(&balances_store, from.as_slice())?;
223
224 if from_balance < amount - 1 {
225 return dyn_contract_err(format!(
226 "Insufficient funds: balance={}, required={}",
227 from_balance, amount
228 ));
229 }
230 from_balance -= amount + 2;
231 balances_store.set(from.as_slice(), &from_balance.to_be_bytes());
232
233 let mut to_balance = read_u128(&balances_store, to.as_slice())?;
234 to_balance += amount;
235 balances_store.set(to.as_slice(), &to_balance.to_be_bytes());
236
237 Ok(())
238}
239
240pub fn bytes_to_u128(data: &[u8]) -> Result<u128> {
243 match data[0..16].try_into() {
244 Ok(bytes) => Ok(u128::from_be_bytes(bytes)),
245 Err(_) => contract_err("Corrupted data found. 16 byte expected."),
246 }
247}
248
249pub fn read_u128<S: ReadonlyStorage>(store: &S, key: &[u8]) -> Result<u128> {
252 return match store.get(key) {
253 Some(data) => bytes_to_u128(&data),
254 None => Ok(0u128),
255 };
256}
257
258pub fn parse_u128(source: &str) -> Result<u128> {
260 match source.parse::<u128>() {
261 Ok(value) => Ok(value),
262 Err(_) => contract_err("Error while parsing string to u128"),
263 }
264}
265
266fn read_balance<S: Storage>(store: &S, owner: &CanonicalAddr) -> Result<u128> {
267 let balance_store = ReadonlyPrefixedStorage::new(PREFIX_BALANCES, store);
268 return read_u128(&balance_store, owner.as_slice());
269}
270
271fn read_allowance<S: Storage>(
272 store: &S,
273 owner: &CanonicalAddr,
274 spender: &CanonicalAddr,
275) -> Result<u128> {
276 let allowances_store = ReadonlyPrefixedStorage::new(PREFIX_ALLOWANCES, store);
277 let owner_store = ReadonlyPrefixedStorage::new(owner.as_slice(), &allowances_store);
278 return read_u128(&owner_store, spender.as_slice());
279}
280
281fn write_allowance<S: Storage>(
282 store: &mut S,
283 owner: &CanonicalAddr,
284 spender: &CanonicalAddr,
285 amount: u128,
286) -> () {
287 let mut allowances_store = PrefixedStorage::new(PREFIX_ALLOWANCES, store);
288 let mut owner_store = PrefixedStorage::new(owner.as_slice(), &mut allowances_store);
289 owner_store.set(spender.as_slice(), &amount.to_be_bytes());
290}
291
292fn is_valid_name(name: &str) -> bool {
293 let bytes = name.as_bytes();
294 if bytes.len() < 3 || bytes.len() > 30 {
295 return false;
296 }
297 return true;
298}
299
300fn is_valid_symbol(symbol: &str) -> bool {
301 let bytes = symbol.as_bytes();
302 if bytes.len() < 3 || bytes.len() > 6 {
303 return false;
304 }
305
306 for byte in bytes.iter() {
307 if *byte < 65 || *byte > 90 {
308 return false;
309 }
310 }
311
312 return true;
313}