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 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 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 let approval = filtered[0].clone();
166
167 Ok(ApprovalResponse { approval })
168 }
169
170 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}