wormhole/cosmwasm/contracts/cw721-base/src/state.rs

142 lines
4.1 KiB
Rust

use schemars::JsonSchema;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use cosmwasm_std::{Addr, BlockInfo, StdResult, Storage};
use cw721::{ContractInfoResponse, CustomMsg, Cw721, Expiration};
use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex};
pub struct Cw721Contract<'a, T, C>
where
T: Serialize + DeserializeOwned + Clone,
{
pub contract_info: Item<'a, ContractInfoResponse>,
pub minter: Item<'a, Addr>,
pub token_count: Item<'a, u64>,
/// Stored as (granter, operator) giving operator full control over granter's account
pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>,
pub tokens: IndexedMap<'a, &'a str, TokenInfo<T>, TokenIndexes<'a, T>>,
pub(crate) _custom_response: PhantomData<C>,
}
// This is a signal, the implementations are in other files
impl<'a, T, C> Cw721<T, C> for Cw721Contract<'a, T, C>
where
T: Serialize + DeserializeOwned + Clone,
C: CustomMsg,
{
}
impl<T, C> Default for Cw721Contract<'static, T, C>
where
T: Serialize + DeserializeOwned + Clone,
{
fn default() -> Self {
Self::new(
"nft_info",
"minter",
"num_tokens",
"operators",
"tokens",
"tokens__owner",
)
}
}
impl<'a, T, C> Cw721Contract<'a, T, C>
where
T: Serialize + DeserializeOwned + Clone,
{
fn new(
contract_key: &'a str,
minter_key: &'a str,
token_count_key: &'a str,
operator_key: &'a str,
tokens_key: &'a str,
tokens_owner_key: &'a str,
) -> Self {
let indexes = TokenIndexes {
owner: MultiIndex::new(token_owner_idx, tokens_key, tokens_owner_key),
};
Self {
contract_info: Item::new(contract_key),
minter: Item::new(minter_key),
token_count: Item::new(token_count_key),
operators: Map::new(operator_key),
tokens: IndexedMap::new(tokens_key, indexes),
_custom_response: PhantomData,
}
}
pub fn token_count(&self, storage: &dyn Storage) -> StdResult<u64> {
Ok(self.token_count.may_load(storage)?.unwrap_or_default())
}
pub fn increment_tokens(&self, storage: &mut dyn Storage) -> StdResult<u64> {
let val = self.token_count(storage)? + 1;
self.token_count.save(storage, &val)?;
Ok(val)
}
pub fn decrement_tokens(&self, storage: &mut dyn Storage) -> StdResult<u64> {
let val = self.token_count(storage)? - 1;
self.token_count.save(storage, &val)?;
Ok(val)
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct TokenInfo<T> {
/// The owner of the newly minted NFT
pub owner: Addr,
/// Approvals are stored here, as we clear them all upon transfer and cannot accumulate much
pub approvals: Vec<Approval>,
/// Universal resource identifier for this NFT
/// Should point to a JSON file that conforms to the ERC721
/// Metadata JSON Schema
pub token_uri: Option<String>,
/// You can add any custom metadata here when you extend cw721-base
pub extension: T,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
pub struct Approval {
/// Account that can transfer/send the token
pub spender: Addr,
/// When the Approval expires (maybe Expiration::never)
pub expires: Expiration,
}
impl Approval {
pub fn is_expired(&self, block: &BlockInfo) -> bool {
self.expires.is_expired(block)
}
}
pub struct TokenIndexes<'a, T>
where
T: Serialize + DeserializeOwned + Clone,
{
// pk goes to second tuple element
pub owner: MultiIndex<'a, (Addr, Vec<u8>), TokenInfo<T>>,
}
impl<'a, T> IndexList<TokenInfo<T>> for TokenIndexes<'a, T>
where
T: Serialize + DeserializeOwned + Clone,
{
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<TokenInfo<T>>> + '_> {
let v: Vec<&dyn Index<TokenInfo<T>>> = vec![&self.owner];
Box::new(v.into_iter())
}
}
pub fn token_owner_idx<T>(d: &TokenInfo<T>, k: Vec<u8>) -> (Addr, Vec<u8>) {
(d.owner.clone(), k)
}