cw721_basic/
state.rs

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