use std::any::type_name;
use std::convert::TryFrom;
use cosmwasm_std::{CanonicalAddr, HumanAddr, ReadonlyStorage, StdError, StdResult, Storage};
use cosmwasm_storage::{PrefixedStorage, ReadonlyPrefixedStorage};
use hermit_toolkit_storage::{TypedStoreMut, TypedStore};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::msg::{status_level_to_u8, u8_to_status_level, ContractStatusLevel};
use crate::viewing_key::ViewingKey;
use serde::de::DeserializeOwned;
pub static CONFIG_KEY: &[u8] = b"config";
pub const PREFIX_TXS: &[u8] = b"transfers";
pub const KEY_CONSTANTS: &[u8] = b"constants";
pub const KEY_TOTAL_SUPPLY: &[u8] = b"total_supply";
pub const KEY_CONTRACT_STATUS: &[u8] = b"contract_status";
pub const KEY_MINTERS: &[u8] = b"minters";
pub const KEY_TX_COUNT: &[u8] = b"tx-count";
pub const PREFIX_CONFIG: &[u8] = b"config";
pub const PREFIX_BALANCES: &[u8] = b"balances";
pub const PREFIX_ALLOWANCES: &[u8] = b"allowances";
pub const PREFIX_VIEW_KEY: &[u8] = b"viewingkey";
pub const PREFIX_RECEIVERS: &[u8] = b"receivers";
#[derive(Serialize, Debug, Deserialize, Clone, PartialEq, JsonSchema)]
pub struct Constants {
pub name: String,
pub admin: HumanAddr,
pub symbol: String,
pub decimals: u8,
pub prng_seed: Vec<u8>,
pub total_supply_is_public: bool,
pub deposit_is_enabled: bool,
pub redeem_is_enabled: bool,
pub mint_is_enabled: bool,
pub burn_is_enabled: bool,
pub contract_address: HumanAddr,
}
pub struct ReadonlyConfig<'a, S: ReadonlyStorage> {
storage: ReadonlyPrefixedStorage<'a, S>,
}
impl<'a, S: ReadonlyStorage> ReadonlyConfig<'a, S> {
pub fn from_storage(storage: &'a S) -> Self {
Self {
storage: ReadonlyPrefixedStorage::new(PREFIX_CONFIG, storage),
}
}
fn as_readonly(&self) -> ReadonlyConfigImpl<ReadonlyPrefixedStorage<S>> {
ReadonlyConfigImpl(&self.storage)
}
pub fn constants(&self) -> StdResult<Constants> {
self.as_readonly().constants()
}
pub fn total_supply(&self) -> u128 {
self.as_readonly().total_supply()
}
pub fn contract_status(&self) -> ContractStatusLevel {
self.as_readonly().contract_status()
}
pub fn minters(&self) -> Vec<HumanAddr> {
self.as_readonly().minters()
}
pub fn tx_count(&self) -> u64 {
self.as_readonly().tx_count()
}
}
fn ser_bin_data<T: Serialize>(obj: &T) -> StdResult<Vec<u8>> {
bincode2::serialize(&obj).map_err(|e| StdError::serialize_err(type_name::<T>(), e))
}
fn deser_bin_data<T: DeserializeOwned>(data: &[u8]) -> StdResult<T> {
bincode2::deserialize::<T>(data).map_err(|e| StdError::serialize_err(type_name::<T>(), e))
}
fn set_bin_data<T: Serialize, S: Storage>(storage: &mut S, key: &[u8], data: &T) -> StdResult<()> {
let bin_data = ser_bin_data(data)?;
storage.set(key, &bin_data);
Ok(())
}
fn get_bin_data<T: DeserializeOwned, S: ReadonlyStorage>(storage: &S, key: &[u8]) -> StdResult<T> {
let bin_data = storage.get(key);
match bin_data {
None => Err(StdError::not_found("Key not found in storage")),
Some(bin_data) => Ok(deser_bin_data(&bin_data)?),
}
}
pub struct Config<'a, S: Storage> {
storage: PrefixedStorage<'a, S>,
}
impl<'a, S: Storage> Config<'a, S> {
pub fn from_storage(storage: &'a mut S) -> Self {
Self {
storage: PrefixedStorage::new(PREFIX_CONFIG, storage),
}
}
fn as_readonly(&self) -> ReadonlyConfigImpl<PrefixedStorage<S>> {
ReadonlyConfigImpl(&self.storage)
}
pub fn constants(&self) -> StdResult<Constants> {
self.as_readonly().constants()
}
pub fn set_constants(&mut self, constants: &Constants) -> StdResult<()> {
set_bin_data(&mut self.storage, KEY_CONSTANTS, constants)
}
pub fn total_supply(&self) -> u128 {
self.as_readonly().total_supply()
}
pub fn set_total_supply(&mut self, supply: u128) {
self.storage.set(KEY_TOTAL_SUPPLY, &supply.to_be_bytes());
}
pub fn contract_status(&self) -> ContractStatusLevel {
self.as_readonly().contract_status()
}
pub fn set_contract_status(&mut self, status: ContractStatusLevel) {
let status_u8 = status_level_to_u8(status);
self.storage
.set(KEY_CONTRACT_STATUS, &status_u8.to_be_bytes());
}
pub fn set_minters(&mut self, minters_to_set: Vec<HumanAddr>) -> StdResult<()> {
set_bin_data(&mut self.storage, KEY_MINTERS, &minters_to_set)
}
pub fn add_minters(&mut self, minters_to_add: Vec<HumanAddr>) -> StdResult<()> {
let mut minters = self.minters();
minters.extend(minters_to_add);
self.set_minters(minters)
}
pub fn remove_minters(&mut self, minters_to_remove: Vec<HumanAddr>) -> StdResult<()> {
let mut minters = self.minters();
for minter in minters_to_remove {
minters.retain(|x| x != &minter);
}
self.set_minters(minters)
}
pub fn minters(&mut self) -> Vec<HumanAddr> {
self.as_readonly().minters()
}
pub fn tx_count(&self) -> u64 {
self.as_readonly().tx_count()
}
pub fn set_tx_count(&mut self, count: u64) -> StdResult<()> {
set_bin_data(&mut self.storage, KEY_TX_COUNT, &count)
}
}
struct ReadonlyConfigImpl<'a, S: ReadonlyStorage>(&'a S);
impl<'a, S: ReadonlyStorage> ReadonlyConfigImpl<'a, S> {
fn constants(&self) -> StdResult<Constants> {
let consts_bytes = self
.0
.get(KEY_CONSTANTS)
.ok_or_else(|| StdError::generic_err("no constants stored in configuration"))?;
bincode2::deserialize::<Constants>(&consts_bytes)
.map_err(|e| StdError::serialize_err(type_name::<Constants>(), e))
}
fn total_supply(&self) -> u128 {
let supply_bytes = self
.0
.get(KEY_TOTAL_SUPPLY)
.expect("no total supply stored in config");
slice_to_u128(&supply_bytes).unwrap()
}
fn contract_status(&self) -> ContractStatusLevel {
let supply_bytes = self
.0
.get(KEY_CONTRACT_STATUS)
.expect("no contract status stored in config");
let status = slice_to_u8(&supply_bytes).unwrap();
u8_to_status_level(status).unwrap()
}
fn minters(&self) -> Vec<HumanAddr> {
get_bin_data(self.0, KEY_MINTERS).unwrap()
}
pub fn tx_count(&self) -> u64 {
get_bin_data(self.0, KEY_TX_COUNT).unwrap_or_default()
}
}
pub struct ReadonlyBalances<'a, S: ReadonlyStorage> {
storage: ReadonlyPrefixedStorage<'a, S>,
}
impl<'a, S: ReadonlyStorage> ReadonlyBalances<'a, S> {
pub fn from_storage(storage: &'a S) -> Self {
Self {
storage: ReadonlyPrefixedStorage::new(PREFIX_BALANCES, storage),
}
}
fn as_readonly(&self) -> ReadonlyBalancesImpl<ReadonlyPrefixedStorage<S>> {
ReadonlyBalancesImpl(&self.storage)
}
pub fn account_amount(&self, account: &CanonicalAddr) -> u128 {
self.as_readonly().account_amount(account)
}
}
pub struct Balances<'a, S: Storage> {
storage: PrefixedStorage<'a, S>,
}
impl<'a, S: Storage> Balances<'a, S> {
pub fn from_storage(storage: &'a mut S) -> Self {
Self {
storage: PrefixedStorage::new(PREFIX_BALANCES, storage),
}
}
fn as_readonly(&self) -> ReadonlyBalancesImpl<PrefixedStorage<S>> {
ReadonlyBalancesImpl(&self.storage)
}
pub fn balance(&self, account: &CanonicalAddr) -> u128 {
self.as_readonly().account_amount(account)
}
pub fn set_account_balance(&mut self, account: &CanonicalAddr, amount: u128) {
self.storage.set(account.as_slice(), &amount.to_be_bytes())
}
}
struct ReadonlyBalancesImpl<'a, S: ReadonlyStorage>(&'a S);
impl<'a, S: ReadonlyStorage> ReadonlyBalancesImpl<'a, S> {
pub fn account_amount(&self, account: &CanonicalAddr) -> u128 {
let account_bytes = account.as_slice();
let result = self.0.get(account_bytes);
match result {
Some(balance_bytes) => slice_to_u128(&balance_bytes).unwrap(),
None => 0,
}
}
}
#[derive(Serialize, Debug, Deserialize, Clone, PartialEq, Default, JsonSchema)]
pub struct Allowance {
pub amount: u128,
pub expiration: Option<u64>,
}
impl Allowance {
pub fn is_expired_at(&self, block: &cosmwasm_std::BlockInfo) -> bool {
match self.expiration {
Some(time) => block.time >= time,
None => false, }
}
}
pub fn read_allowance<S: Storage>(
store: &S,
owner: &CanonicalAddr,
spender: &CanonicalAddr,
) -> StdResult<Allowance> {
let owner_store =
ReadonlyPrefixedStorage::multilevel(&[PREFIX_ALLOWANCES, owner.as_slice()], store);
let owner_store = TypedStore::attach(&owner_store);
let allowance = owner_store.may_load(spender.as_slice());
allowance.map(Option::unwrap_or_default)
}
pub fn write_allowance<S: Storage>(
store: &mut S,
owner: &CanonicalAddr,
spender: &CanonicalAddr,
allowance: Allowance,
) -> StdResult<()> {
let mut owner_store =
PrefixedStorage::multilevel(&[PREFIX_ALLOWANCES, owner.as_slice()], store);
let mut owner_store = TypedStoreMut::attach(&mut owner_store);
owner_store.store(spender.as_slice(), &allowance)
}
pub fn write_viewing_key<S: Storage>(store: &mut S, owner: &CanonicalAddr, key: &ViewingKey) {
let mut balance_store = PrefixedStorage::new(PREFIX_VIEW_KEY, store);
balance_store.set(owner.as_slice(), &key.to_hashed());
}
pub fn read_viewing_key<S: Storage>(store: &S, owner: &CanonicalAddr) -> Option<Vec<u8>> {
let balance_store = ReadonlyPrefixedStorage::new(PREFIX_VIEW_KEY, store);
balance_store.get(owner.as_slice())
}
pub fn get_receiver_hash<S: ReadonlyStorage>(
store: &S,
account: &HumanAddr,
) -> Option<StdResult<String>> {
let store = ReadonlyPrefixedStorage::new(PREFIX_RECEIVERS, store);
store.get(account.as_str().as_bytes()).map(|data| {
String::from_utf8(data)
.map_err(|_err| StdError::invalid_utf8("stored code hash was not a valid String"))
})
}
pub fn set_receiver_hash<S: Storage>(store: &mut S, account: &HumanAddr, code_hash: String) {
let mut store = PrefixedStorage::new(PREFIX_RECEIVERS, store);
store.set(account.as_str().as_bytes(), code_hash.as_bytes());
}
fn slice_to_u128(data: &[u8]) -> StdResult<u128> {
match <[u8; 16]>::try_from(data) {
Ok(bytes) => Ok(u128::from_be_bytes(bytes)),
Err(_) => Err(StdError::generic_err(
"Corrupted data found. 16 byte expected.",
)),
}
}
fn slice_to_u8(data: &[u8]) -> StdResult<u8> {
if data.len() == 1 {
Ok(data[0])
} else {
Err(StdError::generic_err(
"Corrupted data found. 1 byte expected.",
))
}
}