#![allow(clippy::field_reassign_with_default)]
use hermit_toolkit_permit::Permit;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use cosmwasm_std::{Binary, HumanAddr, StdError, StdResult, Uint128};
use crate::batch;
use crate::transaction_history::{RichTx, Tx};
use crate::viewing_key::ViewingKey;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InitHook {
pub msg: Binary,
pub contract_addr: HumanAddr,
pub code_hash: String,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
pub struct InitialBalance {
pub address: HumanAddr,
pub amount: Uint128,
}
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct InitMsg {
pub name: String,
pub admin: Option<HumanAddr>,
pub symbol: String,
pub decimals: u8,
pub initial_balances: Option<Vec<InitialBalance>>,
pub prng_seed: Binary,
pub config: Option<InitConfig>,
pub init_hook: Option<InitHook>,
}
impl InitMsg {
pub fn config(&self) -> InitConfig {
self.config.clone().unwrap_or_default()
}
}
#[derive(Serialize, Deserialize, JsonSchema, Clone, Default, Debug)]
#[serde(rename_all = "snake_case")]
pub struct InitConfig {
public_total_supply: Option<bool>,
enable_deposit: Option<bool>,
enable_redeem: Option<bool>,
enable_mint: Option<bool>,
enable_burn: Option<bool>,
}
impl InitConfig {
pub fn public_total_supply(&self) -> bool {
self.public_total_supply.unwrap_or(false)
}
pub fn deposit_enabled(&self) -> bool {
self.enable_deposit.unwrap_or(false)
}
pub fn redeem_enabled(&self) -> bool {
self.enable_redeem.unwrap_or(false)
}
pub fn mint_enabled(&self) -> bool {
self.enable_mint.unwrap_or(false)
}
pub fn burn_enabled(&self) -> bool {
self.enable_burn.unwrap_or(false)
}
}
#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum HandleMsg {
Redeem {
amount: Uint128,
denom: Option<String>,
padding: Option<String>,
},
Deposit {
padding: Option<String>,
},
Transfer {
recipient: HumanAddr,
amount: Uint128,
memo: Option<String>,
padding: Option<String>,
},
Send {
recipient: HumanAddr,
recipient_code_hash: Option<String>,
amount: Uint128,
msg: Option<Binary>,
memo: Option<String>,
padding: Option<String>,
},
BatchTransfer {
actions: Vec<batch::TransferAction>,
padding: Option<String>,
},
BatchSend {
actions: Vec<batch::SendAction>,
padding: Option<String>,
},
Burn {
amount: Uint128,
memo: Option<String>,
padding: Option<String>,
},
RegisterReceive {
code_hash: String,
padding: Option<String>,
},
CreateViewingKey {
entropy: String,
padding: Option<String>,
},
SetViewingKey {
key: String,
padding: Option<String>,
},
IncreaseAllowance {
spender: HumanAddr,
amount: Uint128,
expiration: Option<u64>,
padding: Option<String>,
},
DecreaseAllowance {
spender: HumanAddr,
amount: Uint128,
expiration: Option<u64>,
padding: Option<String>,
},
TransferFrom {
owner: HumanAddr,
recipient: HumanAddr,
amount: Uint128,
memo: Option<String>,
padding: Option<String>,
},
SendFrom {
owner: HumanAddr,
recipient: HumanAddr,
recipient_code_hash: Option<String>,
amount: Uint128,
msg: Option<Binary>,
memo: Option<String>,
padding: Option<String>,
},
BatchTransferFrom {
actions: Vec<batch::TransferFromAction>,
padding: Option<String>,
},
BatchSendFrom {
actions: Vec<batch::SendFromAction>,
padding: Option<String>,
},
BurnFrom {
owner: HumanAddr,
amount: Uint128,
memo: Option<String>,
padding: Option<String>,
},
BatchBurnFrom {
actions: Vec<batch::BurnFromAction>,
padding: Option<String>,
},
Mint {
recipient: HumanAddr,
amount: Uint128,
memo: Option<String>,
padding: Option<String>,
},
BatchMint {
actions: Vec<batch::MintAction>,
padding: Option<String>,
},
AddMinters {
minters: Vec<HumanAddr>,
padding: Option<String>,
},
RemoveMinters {
minters: Vec<HumanAddr>,
padding: Option<String>,
},
SetMinters {
minters: Vec<HumanAddr>,
padding: Option<String>,
},
ChangeAdmin {
address: HumanAddr,
padding: Option<String>,
},
SetContractStatus {
level: ContractStatusLevel,
padding: Option<String>,
},
RevokePermit {
permit_name: String,
padding: Option<String>,
},
}
#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum HandleAnswer {
Deposit {
status: ResponseStatus,
},
Redeem {
status: ResponseStatus,
},
Transfer {
status: ResponseStatus,
},
Send {
status: ResponseStatus,
},
BatchTransfer {
status: ResponseStatus,
},
BatchSend {
status: ResponseStatus,
},
Burn {
status: ResponseStatus,
},
RegisterReceive {
status: ResponseStatus,
},
CreateViewingKey {
key: ViewingKey,
},
SetViewingKey {
status: ResponseStatus,
},
IncreaseAllowance {
spender: HumanAddr,
owner: HumanAddr,
allowance: Uint128,
},
DecreaseAllowance {
spender: HumanAddr,
owner: HumanAddr,
allowance: Uint128,
},
TransferFrom {
status: ResponseStatus,
},
SendFrom {
status: ResponseStatus,
},
BatchTransferFrom {
status: ResponseStatus,
},
BatchSendFrom {
status: ResponseStatus,
},
BurnFrom {
status: ResponseStatus,
},
BatchBurnFrom {
status: ResponseStatus,
},
Mint {
status: ResponseStatus,
},
BatchMint {
status: ResponseStatus,
},
AddMinters {
status: ResponseStatus,
},
RemoveMinters {
status: ResponseStatus,
},
SetMinters {
status: ResponseStatus,
},
ChangeAdmin {
status: ResponseStatus,
},
SetContractStatus {
status: ResponseStatus,
},
RevokePermit {
status: ResponseStatus,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
TokenInfo {},
TokenConfig {},
ContractStatus {},
ExchangeRate {},
Allowance {
owner: HumanAddr,
spender: HumanAddr,
key: String,
},
Balance {
address: HumanAddr,
key: String,
},
TransferHistory {
address: HumanAddr,
key: String,
page: Option<u32>,
page_size: u32,
},
TransactionHistory {
address: HumanAddr,
key: String,
page: Option<u32>,
page_size: u32,
},
Minters {},
WithPermit {
permit: Permit,
query: QueryWithPermit,
},
}
impl QueryMsg {
pub fn get_validation_params(&self) -> (Vec<&HumanAddr>, ViewingKey) {
match self {
Self::Balance { address, key } => (vec![address], ViewingKey(key.clone())),
Self::TransferHistory { address, key, .. } => (vec![address], ViewingKey(key.clone())),
Self::TransactionHistory { address, key, .. } => {
(vec![address], ViewingKey(key.clone()))
}
Self::Allowance {
owner,
spender,
key,
..
} => (vec![owner, spender], ViewingKey(key.clone())),
_ => panic!("This query type does not require authentication"),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryWithPermit {
Allowance {
owner: HumanAddr,
spender: HumanAddr,
},
Balance {},
TransferHistory {
page: Option<u32>,
page_size: u32,
},
TransactionHistory {
page: Option<u32>,
page_size: u32,
},
}
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum QueryAnswer {
TokenInfo {
name: String,
symbol: String,
decimals: u8,
total_supply: Option<Uint128>,
},
TokenConfig {
public_total_supply: bool,
deposit_enabled: bool,
redeem_enabled: bool,
mint_enabled: bool,
burn_enabled: bool,
},
ContractStatus {
status: ContractStatusLevel,
},
ExchangeRate {
rate: Uint128,
denom: String,
},
Allowance {
spender: HumanAddr,
owner: HumanAddr,
allowance: Uint128,
expiration: Option<u64>,
},
Balance {
amount: Uint128,
},
TransferHistory {
txs: Vec<Tx>,
total: Option<u64>,
},
TransactionHistory {
txs: Vec<RichTx>,
total: Option<u64>,
},
ViewingKeyError {
msg: String,
},
Minters {
minters: Vec<HumanAddr>,
},
}
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
pub struct CreateViewingKeyResponse {
pub key: String,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ResponseStatus {
Success,
Failure,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ContractStatusLevel {
NormalRun,
StopAllButRedeems,
StopAll,
}
pub fn status_level_to_u8(status_level: ContractStatusLevel) -> u8 {
match status_level {
ContractStatusLevel::NormalRun => 0,
ContractStatusLevel::StopAllButRedeems => 1,
ContractStatusLevel::StopAll => 2,
}
}
pub fn u8_to_status_level(status_level: u8) -> StdResult<ContractStatusLevel> {
match status_level {
0 => Ok(ContractStatusLevel::NormalRun),
1 => Ok(ContractStatusLevel::StopAllButRedeems),
2 => Ok(ContractStatusLevel::StopAll),
_ => Err(StdError::generic_err("Invalid state level")),
}
}
pub fn space_pad(block_size: usize, message: &mut Vec<u8>) -> &mut Vec<u8> {
let len = message.len();
let surplus = len % block_size;
if surplus == 0 {
return message;
}
let missing = block_size - surplus;
message.reserve(missing);
message.extend(std::iter::repeat(b' ').take(missing));
message
}
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::{from_slice, StdResult};
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum Something {
Var { padding: Option<String> },
}
#[test]
fn test_deserialization_of_missing_option_fields() -> StdResult<()> {
let input = b"{ \"var\": {} }";
let obj: Something = from_slice(input)?;
assert_eq!(
obj,
Something::Var { padding: None },
"unexpected value: {:?}",
obj
);
Ok(())
}
}