bs721_base/
query.rs

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