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 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
29impl<'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 pub owner: Addr,
107 pub approvals: Vec<Approval>,
109
110 pub token_uri: Option<String>,
114
115 pub seller_fee_bps: Option<u16>,
119
120 pub payment_addr: Option<Addr>,
122
123 pub extension: T,
125}
126
127#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
128pub struct Approval {
129 pub spender: Addr,
131 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}