anone_cw721/
query.rs

1use serde::de::DeserializeOwned;
2use serde::Serialize;
3
4use cosmwasm_std::{to_binary, Addr, Binary, BlockInfo, Deps, Env, Order, StdError, StdResult};
5
6use cw721::{CustomMsg, Expiration};
7use cw_storage_plus::Bound;
8use cw_utils::maybe_addr;
9
10use crate::msg::{
11    AllModelsResponse, AllNftInfoResponse, AllNftsResponse, ApprovalResponse, ApprovalsResponse,
12    CollectionInfoResponse, ContractInfoResponse, MinterResponse, ModelInfoResponse,
13    ModelsResponse, NftInfoResponse, NumModelsResponse, NumTokensResponse, OperatorsResponse,
14    OwnerOfResponse, QueryMsg, RoyaltyInfoResponse, TokensResponse,
15};
16use crate::state::COLLECTION_INFO;
17use crate::state::{AnoneCw721Contract, Approval, ModelInfo, TokenInfo};
18
19const DEFAULT_LIMIT: u32 = 10;
20const MAX_LIMIT: u32 = 30;
21
22impl<'a, T, C> AnoneCw721Contract<'a, T, C>
23where
24    T: Serialize + DeserializeOwned + Clone,
25    C: CustomMsg,
26{
27    pub fn minter(&self, deps: Deps) -> StdResult<MinterResponse> {
28        let minter_addr = self.minter.load(deps.storage)?;
29        Ok(MinterResponse {
30            minter: minter_addr.to_string(),
31        })
32    }
33
34    fn contract_info(&self, deps: Deps) -> StdResult<ContractInfoResponse> {
35        self.contract_info.load(deps.storage)
36    }
37
38    fn num_tokens(&self, deps: Deps) -> StdResult<NumTokensResponse> {
39        let count = self.token_count(deps.storage)?;
40        Ok(NumTokensResponse { count })
41    }
42
43    fn num_models(&self, deps: Deps) -> StdResult<NumModelsResponse> {
44        let count = self.model_count(deps.storage)?;
45        Ok(NumModelsResponse { count })
46    }
47
48    fn owner_of(
49        &self,
50        deps: Deps,
51        env: Env,
52        token_id: String,
53        include_expired: bool,
54    ) -> StdResult<OwnerOfResponse> {
55        let info = self.tokens.load(deps.storage, &token_id)?;
56        Ok(OwnerOfResponse {
57            owner: info.owner.to_string(),
58            approvals: humanize_approvals(&env.block, &info, include_expired),
59        })
60    }
61
62    fn nft_info(&self, deps: Deps, token_id: String) -> StdResult<NftInfoResponse<T>> {
63        let info = self.tokens.load(deps.storage, &token_id)?;
64        Ok(NftInfoResponse {
65            model_id: info.model_id,
66            token_uri: info.token_uri,
67            size: info.size,
68            extension: info.extension,
69        })
70    }
71
72    fn all_nft_info(
73        &self,
74        deps: Deps,
75        env: Env,
76        token_id: String,
77        include_expired: bool,
78    ) -> StdResult<AllNftInfoResponse<T>> {
79        let info = self.tokens.load(deps.storage, &token_id)?;
80        Ok(AllNftInfoResponse {
81            access: OwnerOfResponse {
82                owner: info.owner.to_string(),
83                approvals: humanize_approvals(&env.block, &info, include_expired),
84            },
85            info: NftInfoResponse {
86                model_id: info.model_id,
87                token_uri: info.token_uri,
88                size: info.size,
89                extension: info.extension,
90            },
91        })
92    }
93
94    fn model_info(&self, deps: Deps, model_id: String) -> StdResult<ModelInfoResponse<T>> {
95        let info = self.models.load(deps.storage, &model_id)?;
96        Ok(ModelInfoResponse {
97            owner: info.owner.to_string(),
98            model_uri: info.model_uri,
99            extension: info.extension,
100        })
101    }
102
103    /// operators returns all operators owner given access to
104    fn operators(
105        &self,
106        deps: Deps,
107        env: Env,
108        owner: String,
109        include_expired: bool,
110        start_after: Option<String>,
111        limit: Option<u32>,
112    ) -> StdResult<OperatorsResponse> {
113        let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
114        let start_addr = maybe_addr(deps.api, start_after)?;
115        let start = start_addr.map(|addr| Bound::exclusive(addr.as_ref()));
116
117        let owner_addr = deps.api.addr_validate(&owner)?;
118        let res: StdResult<Vec<_>> = self
119            .operators
120            .prefix(&owner_addr)
121            .range(deps.storage, start, None, Order::Ascending)
122            .filter(|r| {
123                include_expired || r.is_err() || !r.as_ref().unwrap().1.is_expired(&env.block)
124            })
125            .take(limit)
126            .map(parse_approval)
127            .collect();
128        Ok(OperatorsResponse { operators: res? })
129    }
130
131    fn approval(
132        &self,
133        deps: Deps,
134        env: Env,
135        token_id: String,
136        spender: String,
137        include_expired: bool,
138    ) -> StdResult<ApprovalResponse> {
139        let token = self.tokens.load(deps.storage, &token_id)?;
140
141        // token owner has absolute approval
142        if token.owner == spender {
143            let approval = Approval {
144                spender: token.owner,
145                expires: Expiration::Never {},
146            };
147            return Ok(ApprovalResponse { approval });
148        }
149
150        let filtered: Vec<_> = token
151            .approvals
152            .into_iter()
153            .filter(|t| t.spender == spender)
154            .filter(|t| include_expired || !t.is_expired(&env.block))
155            .map(|a| Approval {
156                spender: a.spender,
157                expires: a.expires,
158            })
159            .collect();
160
161        if filtered.is_empty() {
162            return Err(StdError::not_found("Approval not found"));
163        }
164        // we expect only one item
165        let approval = filtered[0].clone();
166
167        Ok(ApprovalResponse { approval })
168    }
169
170    /// approvals returns all approvals owner given access to
171    fn approvals(
172        &self,
173        deps: Deps,
174        env: Env,
175        token_id: String,
176        include_expired: bool,
177    ) -> StdResult<ApprovalsResponse> {
178        let token = self.tokens.load(deps.storage, &token_id)?;
179        let approvals: Vec<_> = token
180            .approvals
181            .into_iter()
182            .filter(|t| include_expired || !t.is_expired(&env.block))
183            .map(|a| Approval {
184                spender: a.spender,
185                expires: a.expires,
186            })
187            .collect();
188
189        Ok(ApprovalsResponse { approvals })
190    }
191
192    fn tokens(
193        &self,
194        deps: Deps,
195        owner: String,
196        start_after: Option<String>,
197        limit: Option<u32>,
198    ) -> StdResult<TokensResponse> {
199        let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
200        let start = start_after.map(Bound::exclusive);
201
202        let owner_addr = deps.api.addr_validate(&owner)?;
203        let tokens: Vec<String> = self
204            .tokens
205            .idx
206            .owner
207            .prefix(owner_addr)
208            .keys(deps.storage, start, None, Order::Ascending)
209            .take(limit)
210            .map(|x| x.map(|addr| addr.to_string()))
211            .collect::<StdResult<Vec<_>>>()?;
212
213        Ok(TokensResponse { tokens })
214    }
215
216    fn all_tokens(
217        &self,
218        deps: Deps,
219        start_after: Option<String>,
220        limit: Option<u32>,
221    ) -> StdResult<TokensResponse> {
222        let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
223        let start = start_after.map(Bound::exclusive);
224
225        let tokens: StdResult<Vec<String>> = self
226            .tokens
227            .range(deps.storage, start, None, Order::Ascending)
228            .take(limit)
229            .map(|item| item.map(|(k, _)| k))
230            .collect();
231
232        Ok(TokensResponse { tokens: tokens? })
233    }
234
235    fn all_tokens_info(
236        &self,
237        deps: Deps,
238        start_after: Option<String>,
239        limit: Option<u32>,
240    ) -> StdResult<AllNftsResponse<T>> {
241        let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
242        let start = start_after.map(Bound::exclusive);
243
244        let tokens: StdResult<Vec<TokenInfo<T>>> = self
245            .tokens
246            .range(deps.storage, start, None, Order::Ascending)
247            .take(limit)
248            .map(|item| item.map(|(_, info)| info))
249            .collect();
250
251        Ok(AllNftsResponse {
252            all_tokens_info: tokens?,
253        })
254    }
255
256    fn all_models(
257        &self,
258        deps: Deps,
259        start_after: Option<String>,
260        limit: Option<u32>,
261    ) -> StdResult<ModelsResponse> {
262        let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
263        let start = start_after.map(Bound::exclusive);
264
265        let models: StdResult<Vec<String>> = self
266            .models
267            .range(deps.storage, start, None, Order::Ascending)
268            .take(limit)
269            .map(|item| item.map(|(k, _)| k))
270            .collect();
271
272        Ok(ModelsResponse { models: models? })
273    }
274
275    fn all_models_info(
276        &self,
277        deps: Deps,
278        start_after: Option<String>,
279        limit: Option<u32>,
280    ) -> StdResult<AllModelsResponse<T>> {
281        let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
282        let start = start_after.map(Bound::exclusive);
283
284        let models: StdResult<Vec<ModelInfo<T>>> = self
285            .models
286            .range(deps.storage, start, None, Order::Ascending)
287            .take(limit)
288            .map(|item| item.map(|(_, info)| info))
289            .collect();
290
291        Ok(AllModelsResponse {
292            all_models_info: models?,
293        })
294    }
295
296    pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
297        match msg {
298            QueryMsg::Minter {} => to_binary(&self.minter(deps)?),
299            QueryMsg::ContractInfo {} => to_binary(&self.contract_info(deps)?),
300            QueryMsg::NftInfo { token_id } => to_binary(&self.nft_info(deps, token_id)?),
301            QueryMsg::OwnerOf {
302                token_id,
303                include_expired,
304            } => {
305                to_binary(&self.owner_of(deps, env, token_id, include_expired.unwrap_or(false))?)
306            }
307            QueryMsg::AllNftInfo {
308                token_id,
309                include_expired,
310            } => to_binary(&self.all_nft_info(
311                deps,
312                env,
313                token_id,
314                include_expired.unwrap_or(false),
315            )?),
316            QueryMsg::ModelInfo { model_id } => to_binary(&self.model_info(deps, model_id)?),
317            QueryMsg::AllOperators {
318                owner,
319                include_expired,
320                start_after,
321                limit,
322            } => to_binary(&self.operators(
323                deps,
324                env,
325                owner,
326                include_expired.unwrap_or(false),
327                start_after,
328                limit,
329            )?),
330            QueryMsg::NumTokens {} => to_binary(&self.num_tokens(deps)?),
331            QueryMsg::NumModels {} => to_binary(&self.num_models(deps)?),
332            QueryMsg::Tokens {
333                owner,
334                start_after,
335                limit,
336            } => to_binary(&self.tokens(deps, owner, start_after, limit)?),
337            QueryMsg::AllTokens { start_after, limit } => {
338                to_binary(&self.all_tokens(deps, start_after, limit)?)
339            }
340            QueryMsg::AllTokensInfo { start_after, limit } => {
341                to_binary(&self.all_tokens_info(deps, start_after, limit)?)
342            }
343            QueryMsg::AllModels { start_after, limit } => {
344                to_binary(&self.all_models(deps, start_after, limit)?)
345            }
346            QueryMsg::AllModelsInfo { start_after, limit } => {
347                to_binary(&self.all_models_info(deps, start_after, limit)?)
348            }
349            QueryMsg::Approval {
350                token_id,
351                spender,
352                include_expired,
353            } => to_binary(&self.approval(
354                deps,
355                env,
356                token_id,
357                spender,
358                include_expired.unwrap_or(false),
359            )?),
360            QueryMsg::Approvals {
361                token_id,
362                include_expired,
363            } => {
364                to_binary(&self.approvals(deps, env, token_id, include_expired.unwrap_or(false))?)
365            }
366            QueryMsg::CollectionInfo {} => to_binary(&query_config(deps)?),
367        }
368    }
369}
370
371fn query_config(deps: Deps) -> StdResult<CollectionInfoResponse> {
372    let info = COLLECTION_INFO.load(deps.storage)?;
373
374    let royalty_info_res: Option<RoyaltyInfoResponse> = match info.royalty_info {
375        Some(royalty_info) => Some(RoyaltyInfoResponse {
376            payment_address: royalty_info.payment_address.to_string(),
377            share: royalty_info.share,
378        }),
379        None => None,
380    };
381
382    Ok(CollectionInfoResponse {
383        creator: info.creator,
384        description: info.description,
385        image: info.image,
386        external_link: info.external_link,
387        royalty_info: royalty_info_res,
388    })
389}
390
391fn parse_approval(item: StdResult<(Addr, Expiration)>) -> StdResult<Approval> {
392    item.map(|(spender, expires)| Approval {
393        spender: spender,
394        expires,
395    })
396}
397
398fn humanize_approvals<T>(
399    block: &BlockInfo,
400    info: &TokenInfo<T>,
401    include_expired: bool,
402) -> Vec<Approval> {
403    info.approvals
404        .iter()
405        .filter(|apr| include_expired || !apr.is_expired(block))
406        .map(humanize_approval)
407        .collect()
408}
409
410fn humanize_approval(approval: &Approval) -> Approval {
411    Approval {
412        spender: approval.spender.clone(),
413        expires: approval.expires,
414    }
415}