cw721_base_terra/
query.rs

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