bs721_base/
state.rs

1use schemars::JsonSchema;
2use serde::de::DeserializeOwned;
3use serde::{Deserialize, Serialize};
4use std::marker::PhantomData;
5
6use cosmwasm_std::{Addr, BlockInfo, CustomMsg, StdResult, Storage};
7
8use bs721::{ContractInfoResponse, Bs721, Expiration};
9use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex};
10
11pub struct Bs721Contract<'a, T, C, E, Q>
12where
13    T: Serialize + DeserializeOwned + Clone,
14    Q: CustomMsg,
15    E: CustomMsg,
16{
17    pub contract_info: Item<'a, ContractInfoResponse>,
18    pub minter: Item<'a, Addr>,
19    pub token_count: Item<'a, u64>,
20    /// Stored as (granter, operator) giving operator full control over granter's account
21    pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>,
22    pub tokens: IndexedMap<'a, &'a str, TokenInfo<T>, TokenIndexes<'a, T>>,
23
24    pub(crate) _custom_response: PhantomData<C>,
25    pub(crate) _custom_query: PhantomData<Q>,
26    pub(crate) _custom_execute: PhantomData<E>,
27}
28
29// This is a signal, the implementations are in other files
30impl<'a, T, C, E, Q> Bs721<T, C> for Bs721Contract<'a, T, C, E, Q>
31where
32    T: Serialize + DeserializeOwned + Clone,
33    C: CustomMsg,
34    E: CustomMsg,
35    Q: CustomMsg,
36{
37}
38
39impl<T, C, E, Q> Default for Bs721Contract<'static, T, C, E, Q>
40where
41    T: Serialize + DeserializeOwned + Clone,
42    E: CustomMsg,
43    Q: CustomMsg,
44{
45    fn default() -> Self {
46        Self::new(
47            "nft_info",
48            "minter",
49            "num_tokens",
50            "operators",
51            "tokens",
52            "tokens__owner",
53        )
54    }
55}
56
57impl<'a, T, C, E, Q> Bs721Contract<'a, T, C, E, Q>
58where
59    T: Serialize + DeserializeOwned + Clone,
60    E: CustomMsg,
61    Q: CustomMsg,
62{
63    fn new(
64        contract_key: &'a str,
65        minter_key: &'a str,
66        token_count_key: &'a str,
67        operator_key: &'a str,
68        tokens_key: &'a str,
69        tokens_owner_key: &'a str,
70    ) -> Self {
71        let indexes = TokenIndexes {
72            owner: MultiIndex::new(token_owner_idx, tokens_key, tokens_owner_key),
73        };
74        Self {
75            contract_info: Item::new(contract_key),
76            minter: Item::new(minter_key),
77            token_count: Item::new(token_count_key),
78            operators: Map::new(operator_key),
79            tokens: IndexedMap::new(tokens_key, indexes),
80            _custom_response: PhantomData,
81            _custom_execute: PhantomData,
82            _custom_query: PhantomData,
83        }
84    }
85
86    pub fn token_count(&self, storage: &dyn Storage) -> StdResult<u64> {
87        Ok(self.token_count.may_load(storage)?.unwrap_or_default())
88    }
89
90    pub fn increment_tokens(&self, storage: &mut dyn Storage) -> StdResult<u64> {
91        let val = self.token_count(storage)? + 1;
92        self.token_count.save(storage, &val)?;
93        Ok(val)
94    }
95
96    pub fn decrement_tokens(&self, storage: &mut dyn Storage) -> StdResult<u64> {
97        let val = self.token_count(storage)? - 1;
98        self.token_count.save(storage, &val)?;
99        Ok(val)
100    }
101}
102
103#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
104pub struct TokenInfo<T> {
105    /// The owner of the newly minted NFT
106    pub owner: Addr,
107    /// Approvals are stored here, as we clear them all upon transfer and cannot accumulate much
108    pub approvals: Vec<Approval>,
109
110    /// Universal resource identifier for this NFT
111    /// Should point to a JSON file that conforms to the ERC721
112    /// Metadata JSON Schema
113    pub token_uri: Option<String>,
114
115    /// Seller fee basis points, 0-10000
116    /// 0 means no fee, 100 means 1%, 10000 means 100%
117    /// This is the fee paid by the buyer to the original creator
118    pub seller_fee_bps: Option<u16>,
119
120    /// Payment address, is the address that will receive the payment
121    pub payment_addr: Option<Addr>,
122
123    /// You can add any custom metadata here when you extend cw721-base
124    pub extension: T,
125}
126
127#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
128pub struct Approval {
129    /// Account that can transfer/send the token
130    pub spender: Addr,
131    /// When the Approval expires (maybe Expiration::never)
132    pub expires: Expiration,
133}
134
135impl Approval {
136    pub fn is_expired(&self, block: &BlockInfo) -> bool {
137        self.expires.is_expired(block)
138    }
139}
140
141pub struct TokenIndexes<'a, T>
142where
143    T: Serialize + DeserializeOwned + Clone,
144{
145    pub owner: MultiIndex<'a, Addr, TokenInfo<T>, String>,
146}
147
148impl<'a, T> IndexList<TokenInfo<T>> for TokenIndexes<'a, T>
149where
150    T: Serialize + DeserializeOwned + Clone,
151{
152    fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<TokenInfo<T>>> + '_> {
153        let v: Vec<&dyn Index<TokenInfo<T>>> = vec![&self.owner];
154        Box::new(v.into_iter())
155    }
156}
157
158pub fn token_owner_idx<T>(_pk: &[u8], d: &TokenInfo<T>) -> Addr {
159    d.owner.clone()
160}