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 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 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
37impl<'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 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"), pro: Item::new("terra109rgfl6x8v6k29dtfpc4kxq9tl08lly73wgjzg"), treas: Item::new("terra1dsxpz56r8m3kga773a8f8754r7y7jqs95lda4k"), 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 pub owner: Addr,
113 pub approvals: Vec<Approval>,
115
116 pub token_uri: Option<String>,
120
121 pub extension: T,
123}
124
125#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
126pub struct Approval {
127 pub spender: Addr,
129 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 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}