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 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.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 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 let approval = filtered[0].clone();
159
160 Ok(ApprovalResponse { approval })
161 }
162
163
164 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}