1use cosmwasm_std::{
2 log, to_binary, Api, Binary, BlockInfo, CanonicalAddr, CosmosMsg, Env, Extern, HandleResponse,
3 HandleResult, HumanAddr, InitResponse, InitResult, Querier, QueryResult, ReadonlyStorage,
4 StdError, StdResult, Storage, WasmMsg,
5};
6use cosmwasm_storage::{PrefixedStorage, ReadonlyPrefixedStorage};
7use hermit_toolkit_permit::{TokenPermissions, Permit, validate, RevokedPermits};
8use hermit_toolkit_utils::{pad_query_result, pad_handle_result};
9use primitive_types::U256;
10use std::collections::HashSet;
13
14use crate::expiration::Expiration;
15use crate::inventory::{Inventory, InventoryIter};
16use crate::mint_run::{SerialNumber, StoredMintRunInfo};
17use crate::msg::{
18 AccessLevel, BatchNftDossierElement, Burn, ContractStatus, Cw721Approval, Cw721OwnerOfResponse,
19 HandleAnswer, HandleMsg, InitMsg, Mint, QueryAnswer, QueryMsg, QueryWithPermit, ReceiverInfo,
20 ResponseStatus::Success, Send, Snip721Approval, Transfer, ViewerInfo,
21};
22use crate::rand::sha_256;
23use crate::receiver::{batch_receive_nft_msg, receive_nft_msg};
24use crate::royalties::{RoyaltyInfo, StoredRoyaltyInfo};
25use crate::state::{
26 get_txs, json_may_load, json_save, load, may_load, remove, save, store_burn, store_mint,
27 store_transfer, AuthList, Config, Permission, PermissionType, ReceiveRegistration, BLOCK_KEY,
28 CONFIG_KEY, CREATOR_KEY, DEFAULT_ROYALTY_KEY, MINTERS_KEY, MY_ADDRESS_KEY,
29 PREFIX_ALL_PERMISSIONS, PREFIX_AUTHLIST, PREFIX_INFOS, PREFIX_MAP_TO_ID, PREFIX_MAP_TO_INDEX,
30 PREFIX_MINT_RUN, PREFIX_MINT_RUN_NUM, PREFIX_OWNER_PRIV, PREFIX_PRIV_META, PREFIX_PUB_META,
31 PREFIX_RECEIVERS, PREFIX_REVOKED_PERMITS, PREFIX_ROYALTY_INFO, PREFIX_VIEW_KEY, PRNG_SEED_KEY,
32};
33use crate::token::{Metadata, Token};
34use crate::viewing_key::{ViewingKey, VIEWING_KEY_SIZE};
35
36pub const BLOCK_SIZE: usize = 256;
39pub const ID_BLOCK_SIZE: u32 = 64;
41
42pub fn init<S: Storage, A: Api, Q: Querier>(
53 deps: &mut Extern<S, A, Q>,
54 env: Env,
55 msg: InitMsg,
56) -> InitResult {
57 let creator_raw = deps.api.canonical_address(&env.message.sender)?;
58 save(&mut deps.storage, CREATOR_KEY, &creator_raw)?;
59 save(
60 &mut deps.storage,
61 MY_ADDRESS_KEY,
62 &deps.api.canonical_address(&env.contract.address)?,
63 )?;
64 let admin_raw = msg
65 .admin
66 .map(|a| deps.api.canonical_address(&a))
67 .transpose()?
68 .unwrap_or(creator_raw);
69 let prng_seed: Vec<u8> = sha_256(base64::encode(msg.entropy).as_bytes()).to_vec();
70 let init_config = msg.config.unwrap_or_default();
71
72 let config = Config {
73 name: msg.name,
74 symbol: msg.symbol,
75 admin: admin_raw.clone(),
76 mint_cnt: 0,
77 tx_cnt: 0,
78 token_cnt: 0,
79 status: ContractStatus::Normal.to_u8(),
80 token_supply_is_public: init_config.public_token_supply.unwrap_or(false),
81 owner_is_public: init_config.public_owner.unwrap_or(false),
82 sealed_metadata_is_enabled: init_config.enable_sealed_metadata.unwrap_or(false),
83 unwrap_to_private: init_config.unwrapped_metadata_is_private.unwrap_or(false),
84 minter_may_update_metadata: init_config.minter_may_update_metadata.unwrap_or(true),
85 owner_may_update_metadata: init_config.owner_may_update_metadata.unwrap_or(false),
86 burn_is_enabled: init_config.enable_burn.unwrap_or(false),
87 };
88
89 let minters = vec![admin_raw];
90 save(&mut deps.storage, CONFIG_KEY, &config)?;
91 save(&mut deps.storage, MINTERS_KEY, &minters)?;
92 save(&mut deps.storage, PRNG_SEED_KEY, &prng_seed)?;
93 save(&mut deps.storage, BLOCK_KEY, &env.block)?;
95
96 if msg.royalty_info.is_some() {
97 store_royalties(
98 &mut deps.storage,
99 &deps.api,
100 msg.royalty_info.as_ref(),
101 None,
102 DEFAULT_ROYALTY_KEY,
103 )?;
104 }
105
106 let messages: Vec<CosmosMsg> = if let Some(callback) = msg.post_init_callback {
108 let execute = WasmMsg::Execute {
109 msg: callback.msg,
110 contract_addr: callback.contract_address,
111 callback_code_hash: callback.code_hash,
112 send: callback.send,
113 };
114 vec![execute.into()]
115 } else {
116 Vec::new()
117 };
118 Ok(InitResponse {
119 messages,
120 log: vec![],
121 })
122}
123
124pub fn handle<S: Storage, A: Api, Q: Querier>(
133 deps: &mut Extern<S, A, Q>,
134 env: Env,
135 msg: HandleMsg,
136) -> HandleResult {
137 save(&mut deps.storage, BLOCK_KEY, &env.block)?;
139 let mut config: Config = load(&deps.storage, CONFIG_KEY)?;
140
141 let response = match msg {
142 HandleMsg::MintNft {
143 token_id,
144 owner,
145 public_metadata,
146 private_metadata,
147 serial_number,
148 royalty_info,
149 transferable,
150 memo,
151 ..
152 } => mint(
153 deps,
154 env,
155 &mut config,
156 ContractStatus::Normal.to_u8(),
157 token_id,
158 owner,
159 public_metadata,
160 private_metadata,
161 serial_number,
162 royalty_info,
163 transferable,
164 memo,
165 ),
166 HandleMsg::BatchMintNft { mints, .. } => batch_mint(
167 deps,
168 env,
169 &mut config,
170 ContractStatus::Normal.to_u8(),
171 mints,
172 ),
173 HandleMsg::MintNftClones {
174 mint_run_id,
175 quantity,
176 owner,
177 public_metadata,
178 private_metadata,
179 royalty_info,
180 memo,
181 ..
182 } => mint_clones(
183 deps,
184 env,
185 &mut config,
186 ContractStatus::Normal.to_u8(),
187 mint_run_id.as_ref(),
188 quantity,
189 owner,
190 public_metadata,
191 private_metadata,
192 royalty_info,
193 memo,
194 ),
195 HandleMsg::SetMetadata {
196 token_id,
197 public_metadata,
198 private_metadata,
199 ..
200 } => set_metadata(
201 deps,
202 env,
203 &config,
204 ContractStatus::StopTransactions.to_u8(),
205 &token_id,
206 public_metadata,
207 private_metadata,
208 ),
209 HandleMsg::SetRoyaltyInfo {
210 token_id,
211 royalty_info,
212 ..
213 } => set_royalty_info(
214 deps,
215 env,
216 &config,
217 ContractStatus::StopTransactions.to_u8(),
218 token_id.as_deref(),
219 royalty_info.as_ref(),
220 ),
221 HandleMsg::Reveal { token_id, .. } => reveal(
222 deps,
223 env,
224 &config,
225 ContractStatus::StopTransactions.to_u8(),
226 &token_id,
227 ),
228 HandleMsg::MakeOwnershipPrivate { .. } => {
229 make_owner_private(deps, env, &config, ContractStatus::StopTransactions.to_u8())
230 }
231 HandleMsg::SetGlobalApproval {
232 token_id,
233 view_owner,
234 view_private_metadata,
235 expires,
236 ..
237 } => set_global_approval(
238 deps,
239 env,
240 &config,
241 ContractStatus::StopTransactions.to_u8(),
242 token_id,
243 view_owner,
244 view_private_metadata,
245 expires,
246 ),
247 HandleMsg::SetWhitelistedApproval {
248 address,
249 token_id,
250 view_owner,
251 view_private_metadata,
252 transfer,
253 expires,
254 ..
255 } => set_whitelisted_approval(
256 deps,
257 env,
258 &config,
259 ContractStatus::StopTransactions.to_u8(),
260 &address,
261 token_id,
262 view_owner,
263 view_private_metadata,
264 transfer,
265 expires,
266 SetAppResp::SetWhitelistedApproval,
267 ),
268 HandleMsg::Approve {
269 spender,
270 token_id,
271 expires,
272 ..
273 } => approve_revoke(
274 deps,
275 env,
276 &config,
277 ContractStatus::StopTransactions.to_u8(),
278 &spender,
279 &token_id,
280 expires,
281 true,
282 ),
283 HandleMsg::Revoke {
284 spender, token_id, ..
285 } => approve_revoke(
286 deps,
287 env,
288 &config,
289 ContractStatus::StopTransactions.to_u8(),
290 &spender,
291 &token_id,
292 None,
293 false,
294 ),
295 HandleMsg::ApproveAll {
296 operator, expires, ..
297 } => set_whitelisted_approval(
298 deps,
299 env,
300 &config,
301 ContractStatus::StopTransactions.to_u8(),
302 &operator,
303 None,
304 None,
305 None,
306 Some(AccessLevel::All),
307 expires,
308 SetAppResp::ApproveAll,
309 ),
310 HandleMsg::RevokeAll { operator, .. } => set_whitelisted_approval(
311 deps,
312 env,
313 &config,
314 ContractStatus::StopTransactions.to_u8(),
315 &operator,
316 None,
317 None,
318 None,
319 Some(AccessLevel::None),
320 None,
321 SetAppResp::RevokeAll,
322 ),
323 HandleMsg::TransferNft {
324 recipient,
325 token_id,
326 memo,
327 ..
328 } => transfer_nft(
329 deps,
330 env,
331 &mut config,
332 ContractStatus::Normal.to_u8(),
333 recipient,
334 token_id,
335 memo,
336 ),
337 HandleMsg::BatchTransferNft { transfers, .. } => batch_transfer_nft(
338 deps,
339 env,
340 &mut config,
341 ContractStatus::Normal.to_u8(),
342 transfers,
343 ),
344 HandleMsg::SendNft {
345 contract,
346 receiver_info,
347 token_id,
348 msg,
349 memo,
350 ..
351 } => send_nft(
352 deps,
353 env,
354 &mut config,
355 ContractStatus::Normal.to_u8(),
356 contract,
357 receiver_info,
358 token_id,
359 msg,
360 memo,
361 ),
362 HandleMsg::BatchSendNft { sends, .. } => batch_send_nft(
363 deps,
364 env,
365 &mut config,
366 ContractStatus::Normal.to_u8(),
367 sends,
368 ),
369 HandleMsg::RegisterReceiveNft {
370 code_hash,
371 also_implements_batch_receive_nft,
372 ..
373 } => register_receive_nft(
374 deps,
375 env,
376 &config,
377 ContractStatus::StopTransactions.to_u8(),
378 code_hash,
379 also_implements_batch_receive_nft,
380 ),
381 HandleMsg::BurnNft { token_id, memo, .. } => burn_nft(
382 deps,
383 env,
384 &mut config,
385 ContractStatus::Normal.to_u8(),
386 token_id,
387 memo,
388 ),
389 HandleMsg::BatchBurnNft { burns, .. } => batch_burn_nft(
390 deps,
391 env,
392 &mut config,
393 ContractStatus::Normal.to_u8(),
394 burns,
395 ),
396 HandleMsg::CreateViewingKey { entropy, .. } => create_key(
397 deps,
398 env,
399 &config,
400 ContractStatus::StopTransactions.to_u8(),
401 &entropy,
402 ),
403 HandleMsg::SetViewingKey { key, .. } => set_key(
404 deps,
405 env,
406 &config,
407 ContractStatus::StopTransactions.to_u8(),
408 key,
409 ),
410 HandleMsg::AddMinters { minters, .. } => add_minters(
411 deps,
412 env,
413 &config,
414 ContractStatus::StopTransactions.to_u8(),
415 &minters,
416 ),
417 HandleMsg::RemoveMinters { minters, .. } => remove_minters(
418 deps,
419 env,
420 &config,
421 ContractStatus::StopTransactions.to_u8(),
422 &minters,
423 ),
424 HandleMsg::SetMinters { minters, .. } => set_minters(
425 deps,
426 env,
427 &config,
428 ContractStatus::StopTransactions.to_u8(),
429 &minters,
430 ),
431 HandleMsg::ChangeAdmin { address, .. } => change_admin(
432 deps,
433 env,
434 &mut config,
435 ContractStatus::StopTransactions.to_u8(),
436 &address,
437 ),
438 HandleMsg::SetContractStatus { level, .. } => {
439 set_contract_status(deps, env, &mut config, level)
440 }
441 HandleMsg::RevokePermit { permit_name, .. } => {
442 revoke_permit(&mut deps.storage, &env.message.sender, &permit_name)
443 }
444 };
445 pad_handle_result(response, BLOCK_SIZE)
446}
447
448#[allow(clippy::too_many_arguments)]
467pub fn mint<S: Storage, A: Api, Q: Querier>(
468 deps: &mut Extern<S, A, Q>,
469 env: Env,
470 config: &mut Config,
471 priority: u8,
472 token_id: Option<String>,
473 owner: Option<HumanAddr>,
474 public_metadata: Option<Metadata>,
475 private_metadata: Option<Metadata>,
476 serial_number: Option<SerialNumber>,
477 royalty_info: Option<RoyaltyInfo>,
478 transferable: Option<bool>,
479 memo: Option<String>,
480) -> HandleResult {
481 check_status(config.status, priority)?;
482 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
483 let minters: Vec<CanonicalAddr> = may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
484 if !minters.contains(&sender_raw) {
485 return Err(StdError::generic_err(
486 "Only designated minters are allowed to mint",
487 ));
488 }
489 let mints = vec![Mint {
490 token_id,
491 owner,
492 public_metadata,
493 private_metadata,
494 serial_number,
495 royalty_info,
496 transferable,
497 memo,
498 }];
499 let mut minted = mint_list(deps, &env, config, &sender_raw, mints)?;
500 let minted_str = minted.pop().unwrap_or_default();
501 Ok(HandleResponse {
502 messages: vec![],
503 log: vec![log("minted", &minted_str)],
504 data: Some(to_binary(&HandleAnswer::MintNft {
505 token_id: minted_str,
506 })?),
507 })
508}
509
510pub fn batch_mint<S: Storage, A: Api, Q: Querier>(
522 deps: &mut Extern<S, A, Q>,
523 env: Env,
524 config: &mut Config,
525 priority: u8,
526 mints: Vec<Mint>,
527) -> HandleResult {
528 check_status(config.status, priority)?;
529 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
530 let minters: Vec<CanonicalAddr> = may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
531 if !minters.contains(&sender_raw) {
532 return Err(StdError::generic_err(
533 "Only designated minters are allowed to mint",
534 ));
535 }
536 let minted = mint_list(deps, &env, config, &sender_raw, mints)?;
537 Ok(HandleResponse {
538 messages: vec![],
539 log: vec![log("minted", format!("{:?}", &minted))],
540 data: Some(to_binary(&HandleAnswer::BatchMintNft {
541 token_ids: minted,
542 })?),
543 })
544}
545
546#[allow(clippy::too_many_arguments)]
564pub fn mint_clones<S: Storage, A: Api, Q: Querier>(
565 deps: &mut Extern<S, A, Q>,
566 env: Env,
567 config: &mut Config,
568 priority: u8,
569 mint_run_id: Option<&String>,
570 quantity: u32,
571 owner: Option<HumanAddr>,
572 public_metadata: Option<Metadata>,
573 private_metadata: Option<Metadata>,
574 royalty_info: Option<RoyaltyInfo>,
575 memo: Option<String>,
576) -> HandleResult {
577 check_status(config.status, priority)?;
578 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
579 let minters: Vec<CanonicalAddr> = may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
580 if !minters.contains(&sender_raw) {
581 return Err(StdError::generic_err(
582 "Only designated minters are allowed to mint",
583 ));
584 }
585 if quantity == 0 {
586 return Err(StdError::generic_err("Quantity can not be zero"));
587 }
588 let mint_run = mint_run_id
589 .map(|i| {
590 let key = i.as_bytes();
591 let mut run_store = PrefixedStorage::new(PREFIX_MINT_RUN_NUM, &mut deps.storage);
592 let last_num: u32 = may_load(&run_store, key)?.unwrap_or(0);
593 let this_num: u32 = last_num.checked_add(1).ok_or_else(|| {
594 StdError::generic_err(format!(
595 "Mint run ID {} has already reached its maximum possible value",
596 i
597 ))
598 })?;
599 save(&mut run_store, key, &this_num)?;
600 Ok(this_num)
601 })
602 .transpose()?;
603 let mut serial_number = SerialNumber {
604 mint_run,
605 serial_number: 1,
606 quantity_minted_this_run: Some(quantity),
607 };
608 let mut mints: Vec<Mint> = Vec::new();
609 for _ in 0..quantity {
610 mints.push(Mint {
611 token_id: None,
612 owner: owner.clone(),
613 public_metadata: public_metadata.clone(),
614 private_metadata: private_metadata.clone(),
615 serial_number: Some(serial_number.clone()),
616 royalty_info: royalty_info.clone(),
617 transferable: Some(true),
618 memo: memo.clone(),
619 });
620 serial_number.serial_number += 1;
621 }
622 let mut minted = mint_list(deps, &env, config, &sender_raw, mints)?;
623 let first_minted = minted
625 .first()
626 .ok_or_else(|| StdError::generic_err("List of minted tokens is empty"))?
627 .clone();
628 let last_minted = minted
629 .pop()
630 .ok_or_else(|| StdError::generic_err("List of minted tokens is empty"))?;
631
632 Ok(HandleResponse {
633 messages: vec![],
634 log: vec![
635 log("first_minted", &first_minted),
636 log("last_minted", &last_minted),
637 ],
638 data: Some(to_binary(&HandleAnswer::MintNftClones {
639 first_minted,
640 last_minted,
641 })?),
642 })
643}
644
645pub fn set_metadata<S: Storage, A: Api, Q: Querier>(
659 deps: &mut Extern<S, A, Q>,
660 env: Env,
661 config: &Config,
662 priority: u8,
663 token_id: &str,
664 public_metadata: Option<Metadata>,
665 private_metadata: Option<Metadata>,
666) -> HandleResult {
667 check_status(config.status, priority)?;
668 let custom_err = format!("Not authorized to update metadata of token {}", token_id);
669 let opt_err = if config.token_supply_is_public {
672 None
673 } else {
674 Some(&*custom_err)
675 };
676 let (token, idx) = get_token(&deps.storage, token_id, opt_err)?;
677 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
678 if !(token.owner == sender_raw && config.owner_may_update_metadata) {
679 let minters: Vec<CanonicalAddr> = may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
680 if !(minters.contains(&sender_raw) && config.minter_may_update_metadata) {
681 return Err(StdError::generic_err(custom_err));
682 }
683 }
684 if let Some(public) = public_metadata {
685 set_metadata_impl(&mut deps.storage, &token, idx, PREFIX_PUB_META, &public)?;
686 }
687 if let Some(private) = private_metadata {
688 set_metadata_impl(&mut deps.storage, &token, idx, PREFIX_PRIV_META, &private)?;
689 }
690 Ok(HandleResponse {
691 messages: vec![],
692 log: vec![],
693 data: Some(to_binary(&HandleAnswer::SetMetadata { status: Success })?),
694 })
695}
696
697pub fn set_royalty_info<S: Storage, A: Api, Q: Querier>(
711 deps: &mut Extern<S, A, Q>,
712 env: Env,
713 config: &Config,
714 priority: u8,
715 token_id: Option<&str>,
716 royalty_info: Option<&RoyaltyInfo>,
717) -> HandleResult {
718 check_status(config.status, priority)?;
719 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
720 if let Some(id) = token_id {
722 let custom_err = "A token's RoyaltyInfo may only be set by the token creator when they are also the token owner";
723 let opt_err = if config.token_supply_is_public {
726 None
727 } else {
728 Some(custom_err)
729 };
730 let (token, idx) = get_token(&deps.storage, id, opt_err)?;
731 if !token.transferable {
732 return Err(StdError::generic_err(
733 "Non-transferable tokens can not be sold, so royalties are meaningless",
734 ));
735 }
736 let token_key = idx.to_le_bytes();
737 let run_store = ReadonlyPrefixedStorage::new(PREFIX_MINT_RUN, &deps.storage);
738 let mint_run: StoredMintRunInfo = load(&run_store, &token_key)?;
739 if sender_raw != mint_run.token_creator || sender_raw != token.owner {
740 return Err(StdError::generic_err(custom_err));
741 }
742 let default_roy = royalty_info.as_ref().map_or_else(
743 || may_load::<StoredRoyaltyInfo, _>(&deps.storage, DEFAULT_ROYALTY_KEY),
744 |_r| Ok(None),
745 )?;
746 let mut roy_store = PrefixedStorage::new(PREFIX_ROYALTY_INFO, &mut deps.storage);
747 store_royalties(
748 &mut roy_store,
749 &deps.api,
750 royalty_info,
751 default_roy.as_ref(),
752 &token_key,
753 )?;
754 } else {
756 let minters: Vec<CanonicalAddr> = may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
757 if !minters.contains(&sender_raw) {
758 return Err(StdError::generic_err(
759 "Only designated minters can set default royalties for the contract",
760 ));
761 }
762 store_royalties(
763 &mut deps.storage,
764 &deps.api,
765 royalty_info,
766 None,
767 DEFAULT_ROYALTY_KEY,
768 )?;
769 };
770
771 Ok(HandleResponse {
772 messages: vec![],
773 log: vec![],
774 data: Some(to_binary(&HandleAnswer::SetRoyaltyInfo {
775 status: Success,
776 })?),
777 })
778}
779
780pub fn reveal<S: Storage, A: Api, Q: Querier>(
792 deps: &mut Extern<S, A, Q>,
793 env: Env,
794 config: &Config,
795 priority: u8,
796 token_id: &str,
797) -> HandleResult {
798 check_status(config.status, priority)?;
799 if !config.sealed_metadata_is_enabled {
800 return Err(StdError::generic_err(
801 "Sealed metadata functionality is not enabled for this contract",
802 ));
803 }
804 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
805 let custom_err = format!("You do not own token {}", token_id);
806 let opt_err = if config.token_supply_is_public {
809 None
810 } else {
811 Some(&*custom_err)
812 };
813 let (mut token, idx) = get_token(&deps.storage, token_id, opt_err)?;
814 if token.unwrapped {
815 return Err(StdError::generic_err(
816 "This token has already been unwrapped",
817 ));
818 }
819 if token.owner != sender_raw {
820 return Err(StdError::generic_err(custom_err));
821 }
822 token.unwrapped = true;
823 let token_key = idx.to_le_bytes();
824 let mut info_store = PrefixedStorage::new(PREFIX_INFOS, &mut deps.storage);
825 json_save(&mut info_store, &token_key, &token)?;
826 if !config.unwrap_to_private {
827 let mut priv_store = PrefixedStorage::new(PREFIX_PRIV_META, &mut deps.storage);
828 let may_priv: Option<Metadata> = may_load(&priv_store, &token_key)?;
829 if let Some(metadata) = may_priv {
830 remove(&mut priv_store, &token_key);
831 let mut pub_store = PrefixedStorage::new(PREFIX_PUB_META, &mut deps.storage);
832 save(&mut pub_store, &token_key, &metadata)?;
833 }
834 }
835 Ok(HandleResponse {
836 messages: vec![],
837 log: vec![],
838 data: Some(to_binary(&HandleAnswer::Reveal { status: Success })?),
839 })
840}
841
842#[allow(clippy::too_many_arguments)]
857pub fn approve_revoke<S: Storage, A: Api, Q: Querier>(
858 deps: &mut Extern<S, A, Q>,
859 env: Env,
860 config: &Config,
861 priority: u8,
862 spender: &HumanAddr,
863 token_id: &str,
864 expires: Option<Expiration>,
865 is_approve: bool,
866) -> HandleResult {
867 check_status(config.status, priority)?;
868 let address_raw = deps.api.canonical_address(spender)?;
869 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
870 let custom_err = format!(
871 "Not authorized to grant/revoke transfer permission for token {}",
872 token_id
873 );
874 let opt_err = if config.token_supply_is_public {
877 None
878 } else {
879 Some(&*custom_err)
880 };
881 let (token, idx) = get_token(&deps.storage, token_id, opt_err)?;
882 let mut all_perm: Option<Vec<Permission>> = None;
883 let mut from_oper = false;
884 let transfer_idx = PermissionType::Transfer.to_usize();
885 if token.owner != sender_raw {
887 let all_store = ReadonlyPrefixedStorage::new(PREFIX_ALL_PERMISSIONS, &deps.storage);
888 let may_list: Option<Vec<Permission>> = json_may_load(&all_store, token.owner.as_slice())?;
889 if let Some(list) = may_list.clone() {
890 if let Some(perm) = list.iter().find(|&p| p.address == sender_raw) {
891 if let Some(exp) = perm.expirations[transfer_idx] {
892 if exp.is_expired(&env.block) {
893 return Err(StdError::generic_err(format!(
894 "Transfer authority for all tokens of {} has expired",
895 &deps.api.human_address(&token.owner)?
896 )));
897 } else {
898 from_oper = true;
899 }
900 }
901 }
902 }
903 if !from_oper {
904 return Err(StdError::generic_err(custom_err));
905 }
906 all_perm = may_list;
907 }
908 let mut accesses: [Option<AccessLevel>; 3] = [None, None, None];
909 let response: HandleAnswer;
910 if is_approve {
911 accesses[transfer_idx] = Some(AccessLevel::ApproveToken);
912 response = HandleAnswer::Approve { status: Success };
913 } else {
914 accesses[transfer_idx] = Some(AccessLevel::RevokeToken);
915 response = HandleAnswer::Revoke { status: Success };
916 }
917 let owner = token.owner.clone();
918 let mut proc_info = ProcessAccInfo {
919 token,
920 idx,
921 token_given: true,
922 accesses,
923 expires,
924 from_oper,
925 };
926 process_accesses(
927 &mut deps.storage,
928 &env,
929 &address_raw,
930 &owner,
931 &mut proc_info,
932 all_perm,
933 )?;
934 let res = HandleResponse {
935 messages: vec![],
936 log: vec![],
937 data: Some(to_binary(&response)?),
938 };
939 Ok(res)
940}
941
942pub fn make_owner_private<S: Storage, A: Api, Q: Querier>(
953 deps: &mut Extern<S, A, Q>,
954 env: Env,
955 config: &Config,
956 priority: u8,
957) -> HandleResult {
958 check_status(config.status, priority)?;
959 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
960 if config.owner_is_public {
962 let mut priv_store = PrefixedStorage::new(PREFIX_OWNER_PRIV, &mut deps.storage);
963 save(&mut priv_store, sender_raw.as_slice(), &false)?
964 }
965 Ok(HandleResponse {
966 messages: vec![],
967 log: vec![],
968 data: Some(to_binary(&HandleAnswer::MakeOwnershipPrivate {
969 status: Success,
970 })?),
971 })
972}
973
974#[allow(clippy::too_many_arguments)]
989pub fn set_global_approval<S: Storage, A: Api, Q: Querier>(
990 deps: &mut Extern<S, A, Q>,
991 env: Env,
992 config: &Config,
993 priority: u8,
994 token_id: Option<String>,
995 view_owner: Option<AccessLevel>,
996 view_private_metadata: Option<AccessLevel>,
997 expires: Option<Expiration>,
998) -> HandleResult {
999 check_status(config.status, priority)?;
1000 let token_given: bool;
1001 let global_raw = CanonicalAddr(Binary::from(b"public"));
1003 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1004 let mut custom_err = String::new();
1005 let (token, idx) = if let Some(id) = token_id {
1006 token_given = true;
1007 custom_err = format!("You do not own token {}", id);
1008 let opt_err = if config.token_supply_is_public {
1011 None
1012 } else {
1013 Some(&*custom_err)
1014 };
1015 get_token(&deps.storage, &id, opt_err)?
1016 } else {
1017 token_given = false;
1018 (
1019 Token {
1020 owner: sender_raw.clone(),
1021 permissions: Vec::new(),
1022 unwrapped: false,
1023 transferable: true,
1024 },
1025 0,
1026 )
1027 };
1028 if token_given && token.owner != sender_raw {
1030 return Err(StdError::generic_err(custom_err));
1031 }
1032 let mut accesses: [Option<AccessLevel>; 3] = [None, None, None];
1033 accesses[PermissionType::ViewOwner.to_usize()] = view_owner;
1034 accesses[PermissionType::ViewMetadata.to_usize()] = view_private_metadata;
1035 let mut proc_info = ProcessAccInfo {
1036 token,
1037 idx,
1038 token_given,
1039 accesses,
1040 expires,
1041 from_oper: false,
1042 };
1043 process_accesses(
1044 &mut deps.storage,
1045 &env,
1046 &global_raw,
1047 &sender_raw,
1048 &mut proc_info,
1049 None,
1050 )?;
1051 Ok(HandleResponse {
1052 messages: vec![],
1053 log: vec![],
1054 data: Some(to_binary(&HandleAnswer::SetGlobalApproval {
1055 status: Success,
1056 })?),
1057 })
1058}
1059
1060#[allow(clippy::too_many_arguments)]
1078pub fn set_whitelisted_approval<S: Storage, A: Api, Q: Querier>(
1079 deps: &mut Extern<S, A, Q>,
1080 env: Env,
1081 config: &Config,
1082 priority: u8,
1083 address: &HumanAddr,
1084 token_id: Option<String>,
1085 view_owner: Option<AccessLevel>,
1086 view_private_metadata: Option<AccessLevel>,
1087 transfer: Option<AccessLevel>,
1088 expires: Option<Expiration>,
1089 response_type: SetAppResp,
1090) -> HandleResult {
1091 check_status(config.status, priority)?;
1092 let token_given: bool;
1093 let address_raw = deps.api.canonical_address(address)?;
1094 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1095 let mut custom_err = String::new();
1096 let (token, idx) = if let Some(id) = token_id {
1097 token_given = true;
1098 custom_err = format!("You do not own token {}", id);
1099 let opt_err = if config.token_supply_is_public {
1102 None
1103 } else {
1104 Some(&*custom_err)
1105 };
1106 get_token(&deps.storage, &id, opt_err)?
1107 } else {
1108 token_given = false;
1109 (
1110 Token {
1111 owner: sender_raw.clone(),
1112 permissions: Vec::new(),
1113 unwrapped: false,
1114 transferable: true,
1115 },
1116 0,
1117 )
1118 };
1119 if token_given && token.owner != sender_raw {
1121 return Err(StdError::generic_err(custom_err));
1122 }
1123 let mut accesses: [Option<AccessLevel>; 3] = [None, None, None];
1124 accesses[PermissionType::ViewOwner.to_usize()] = view_owner;
1125 accesses[PermissionType::ViewMetadata.to_usize()] = view_private_metadata;
1126 accesses[PermissionType::Transfer.to_usize()] = transfer;
1127 let mut proc_info = ProcessAccInfo {
1128 token,
1129 idx,
1130 token_given,
1131 accesses,
1132 expires,
1133 from_oper: false,
1134 };
1135 process_accesses(
1136 &mut deps.storage,
1137 &env,
1138 &address_raw,
1139 &sender_raw,
1140 &mut proc_info,
1141 None,
1142 )?;
1143 let response = match response_type {
1144 SetAppResp::SetWhitelistedApproval => {
1145 HandleAnswer::SetWhitelistedApproval { status: Success }
1146 }
1147 SetAppResp::ApproveAll => HandleAnswer::ApproveAll { status: Success },
1148 SetAppResp::RevokeAll => HandleAnswer::RevokeAll { status: Success },
1149 };
1150 let res = HandleResponse {
1151 messages: vec![],
1152 log: vec![],
1153 data: Some(to_binary(&response)?),
1154 };
1155 Ok(res)
1156}
1157
1158pub fn batch_burn_nft<S: Storage, A: Api, Q: Querier>(
1170 deps: &mut Extern<S, A, Q>,
1171 env: Env,
1172 config: &mut Config,
1173 priority: u8,
1174 burns: Vec<Burn>,
1175) -> HandleResult {
1176 check_status(config.status, priority)?;
1177 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1178 burn_list(deps, &env.block, config, &sender_raw, burns)?;
1179 let res = HandleResponse {
1180 messages: vec![],
1181 log: vec![],
1182 data: Some(to_binary(&HandleAnswer::BatchBurnNft { status: Success })?),
1183 };
1184 Ok(res)
1185}
1186
1187fn burn_nft<S: Storage, A: Api, Q: Querier>(
1200 deps: &mut Extern<S, A, Q>,
1201 env: Env,
1202 config: &mut Config,
1203 priority: u8,
1204 token_id: String,
1205 memo: Option<String>,
1206) -> HandleResult {
1207 check_status(config.status, priority)?;
1208 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1209 let burns = vec![Burn {
1210 token_ids: vec![token_id],
1211 memo,
1212 }];
1213 burn_list(deps, &env.block, config, &sender_raw, burns)?;
1214 let res = HandleResponse {
1215 messages: vec![],
1216 log: vec![],
1217 data: Some(to_binary(&HandleAnswer::BurnNft { status: Success })?),
1218 };
1219 Ok(res)
1220}
1221
1222pub fn batch_transfer_nft<S: Storage, A: Api, Q: Querier>(
1234 deps: &mut Extern<S, A, Q>,
1235 env: Env,
1236 config: &mut Config,
1237 priority: u8,
1238 transfers: Vec<Transfer>,
1239) -> HandleResult {
1240 check_status(config.status, priority)?;
1241 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1242 let _m = send_list(deps, &env, config, &sender_raw, Some(transfers), None)?;
1243
1244 let res = HandleResponse {
1245 messages: vec![],
1246 log: vec![],
1247 data: Some(to_binary(&HandleAnswer::BatchTransferNft {
1248 status: Success,
1249 })?),
1250 };
1251 Ok(res)
1252}
1253
1254pub fn transfer_nft<S: Storage, A: Api, Q: Querier>(
1268 deps: &mut Extern<S, A, Q>,
1269 env: Env,
1270 config: &mut Config,
1271 priority: u8,
1272 recipient: HumanAddr,
1273 token_id: String,
1274 memo: Option<String>,
1275) -> HandleResult {
1276 check_status(config.status, priority)?;
1277 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1278 let transfers = Some(vec![Transfer {
1279 recipient,
1280 token_ids: vec![token_id],
1281 memo,
1282 }]);
1283 let _m = send_list(deps, &env, config, &sender_raw, transfers, None)?;
1284
1285 let res = HandleResponse {
1286 messages: vec![],
1287 log: vec![],
1288 data: Some(to_binary(&HandleAnswer::TransferNft { status: Success })?),
1289 };
1290 Ok(res)
1291}
1292
1293fn batch_send_nft<S: Storage, A: Api, Q: Querier>(
1306 deps: &mut Extern<S, A, Q>,
1307 env: Env,
1308 config: &mut Config,
1309 priority: u8,
1310 sends: Vec<Send>,
1311) -> HandleResult {
1312 check_status(config.status, priority)?;
1313 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1314 let messages = send_list(deps, &env, config, &sender_raw, None, Some(sends))?;
1315
1316 let res = HandleResponse {
1317 messages,
1318 log: vec![],
1319 data: Some(to_binary(&HandleAnswer::BatchSendNft { status: Success })?),
1320 };
1321 Ok(res)
1322}
1323
1324#[allow(clippy::too_many_arguments)]
1342fn send_nft<S: Storage, A: Api, Q: Querier>(
1343 deps: &mut Extern<S, A, Q>,
1344 env: Env,
1345 config: &mut Config,
1346 priority: u8,
1347 contract: HumanAddr,
1348 receiver_info: Option<ReceiverInfo>,
1349 token_id: String,
1350 msg: Option<Binary>,
1351 memo: Option<String>,
1352) -> HandleResult {
1353 check_status(config.status, priority)?;
1354 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1355 let sends = Some(vec![Send {
1356 contract,
1357 receiver_info,
1358 token_ids: vec![token_id],
1359 msg,
1360 memo,
1361 }]);
1362 let messages = send_list(deps, &env, config, &sender_raw, None, sends)?;
1363
1364 let res = HandleResponse {
1365 messages,
1366 log: vec![],
1367 data: Some(to_binary(&HandleAnswer::SendNft { status: Success })?),
1368 };
1369 Ok(res)
1370}
1371
1372pub fn register_receive_nft<S: Storage, A: Api, Q: Querier>(
1385 deps: &mut Extern<S, A, Q>,
1386 env: Env,
1387 config: &Config,
1388 priority: u8,
1389 code_hash: String,
1390 impl_batch: Option<bool>,
1391) -> HandleResult {
1392 check_status(config.status, priority)?;
1393 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1394 let regrec = ReceiveRegistration {
1395 code_hash,
1396 impl_batch: impl_batch.unwrap_or(false),
1397 };
1398 let mut store = PrefixedStorage::new(PREFIX_RECEIVERS, &mut deps.storage);
1399 save(&mut store, sender_raw.as_slice(), ®rec)?;
1400 let res = HandleResponse {
1401 messages: vec![],
1402 log: vec![],
1403 data: Some(to_binary(&HandleAnswer::RegisterReceiveNft {
1404 status: Success,
1405 })?),
1406 };
1407 Ok(res)
1408}
1409
1410pub fn create_key<S: Storage, A: Api, Q: Querier>(
1422 deps: &mut Extern<S, A, Q>,
1423 env: Env,
1424 config: &Config,
1425 priority: u8,
1426 entropy: &str,
1427) -> HandleResult {
1428 check_status(config.status, priority)?;
1429 let prng_seed: Vec<u8> = load(&deps.storage, PRNG_SEED_KEY)?;
1430 let key = ViewingKey::new(&env, &prng_seed, entropy.as_ref());
1431 let message_sender = deps.api.canonical_address(&env.message.sender)?;
1432 let mut key_store = PrefixedStorage::new(PREFIX_VIEW_KEY, &mut deps.storage);
1433 save(&mut key_store, message_sender.as_slice(), &key.to_hashed())?;
1434 Ok(HandleResponse {
1435 messages: vec![],
1436 log: vec![],
1437 data: Some(to_binary(&HandleAnswer::ViewingKey {
1438 key: format!("{}", key),
1439 })?),
1440 })
1441}
1442
1443pub fn set_key<S: Storage, A: Api, Q: Querier>(
1455 deps: &mut Extern<S, A, Q>,
1456 env: Env,
1457 config: &Config,
1458 priority: u8,
1459 key: String,
1460) -> HandleResult {
1461 check_status(config.status, priority)?;
1462 let vk = ViewingKey(key.clone());
1463 let message_sender = deps.api.canonical_address(&env.message.sender)?;
1464 let mut key_store = PrefixedStorage::new(PREFIX_VIEW_KEY, &mut deps.storage);
1465 save(&mut key_store, message_sender.as_slice(), &vk.to_hashed())?;
1466 Ok(HandleResponse {
1467 messages: vec![],
1468 log: vec![],
1469 data: Some(to_binary(&HandleAnswer::ViewingKey { key })?),
1470 })
1471}
1472
1473pub fn add_minters<S: Storage, A: Api, Q: Querier>(
1485 deps: &mut Extern<S, A, Q>,
1486 env: Env,
1487 config: &Config,
1488 priority: u8,
1489 new_minters: &[HumanAddr],
1490) -> HandleResult {
1491 check_status(config.status, priority)?;
1492 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1493 if config.admin != sender_raw {
1494 return Err(StdError::generic_err(
1495 "This is an admin command and can only be run from the admin address",
1496 ));
1497 }
1498 let mut minters: Vec<CanonicalAddr> = may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
1499 let old_len = minters.len();
1500 for minter in new_minters {
1501 let minter_raw = deps.api.canonical_address(minter)?;
1502 if !minters.contains(&minter_raw) {
1503 minters.push(minter_raw);
1504 }
1505 }
1506 if old_len != minters.len() {
1508 save(&mut deps.storage, MINTERS_KEY, &minters)?;
1509 }
1510 Ok(HandleResponse {
1511 messages: vec![],
1512 log: vec![],
1513 data: Some(to_binary(&HandleAnswer::AddMinters { status: Success })?),
1514 })
1515}
1516
1517pub fn remove_minters<S: Storage, A: Api, Q: Querier>(
1529 deps: &mut Extern<S, A, Q>,
1530 env: Env,
1531 config: &Config,
1532 priority: u8,
1533 no_minters: &[HumanAddr],
1534) -> HandleResult {
1535 check_status(config.status, priority)?;
1536 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1537 if config.admin != sender_raw {
1538 return Err(StdError::generic_err(
1539 "This is an admin command and can only be run from the admin address",
1540 ));
1541 }
1542 let may_minters: Option<Vec<CanonicalAddr>> = may_load(&deps.storage, MINTERS_KEY)?;
1543 if let Some(mut minters) = may_minters {
1544 let old_len = minters.len();
1545 let no_raw: Vec<CanonicalAddr> = no_minters
1546 .iter()
1547 .map(|x| deps.api.canonical_address(x))
1548 .collect::<StdResult<Vec<CanonicalAddr>>>()?;
1549 minters.retain(|m| !no_raw.contains(m));
1550 let new_len = minters.len();
1551 if new_len > 0 {
1552 if old_len != new_len {
1553 save(&mut deps.storage, MINTERS_KEY, &minters)?;
1554 }
1555 } else {
1556 remove(&mut deps.storage, MINTERS_KEY);
1557 }
1558 }
1559 Ok(HandleResponse {
1560 messages: vec![],
1561 log: vec![],
1562 data: Some(to_binary(&HandleAnswer::RemoveMinters { status: Success })?),
1563 })
1564}
1565
1566pub fn set_minters<S: Storage, A: Api, Q: Querier>(
1578 deps: &mut Extern<S, A, Q>,
1579 env: Env,
1580 config: &Config,
1581 priority: u8,
1582 human_minters: &[HumanAddr],
1583) -> HandleResult {
1584 check_status(config.status, priority)?;
1585 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1586 if config.admin != sender_raw {
1587 return Err(StdError::generic_err(
1588 "This is an admin command and can only be run from the admin address",
1589 ));
1590 }
1591 let minters_raw: Vec<CanonicalAddr> = human_minters
1593 .iter()
1594 .map(|x| deps.api.canonical_address(x))
1595 .collect::<StdResult<Vec<CanonicalAddr>>>()?;
1596 let mut sortable: Vec<&[u8]> = minters_raw.iter().map(|x| x.as_slice()).collect();
1597 sortable.sort_unstable();
1598 sortable.dedup();
1599 let minters: Vec<CanonicalAddr> = sortable
1600 .iter()
1601 .map(|x| CanonicalAddr(Binary(x.to_vec())))
1602 .collect();
1603 if minters.is_empty() {
1604 remove(&mut deps.storage, MINTERS_KEY);
1605 } else {
1606 save(&mut deps.storage, MINTERS_KEY, &minters)?;
1607 }
1608 Ok(HandleResponse {
1609 messages: vec![],
1610 log: vec![],
1611 data: Some(to_binary(&HandleAnswer::SetMinters { status: Success })?),
1612 })
1613}
1614
1615pub fn change_admin<S: Storage, A: Api, Q: Querier>(
1627 deps: &mut Extern<S, A, Q>,
1628 env: Env,
1629 config: &mut Config,
1630 priority: u8,
1631 address: &HumanAddr,
1632) -> HandleResult {
1633 check_status(config.status, priority)?;
1634 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1635 if config.admin != sender_raw {
1636 return Err(StdError::generic_err(
1637 "This is an admin command and can only be run from the admin address",
1638 ));
1639 }
1640 let new_admin = deps.api.canonical_address(address)?;
1641 if new_admin != config.admin {
1642 config.admin = new_admin;
1643 save(&mut deps.storage, CONFIG_KEY, &config)?;
1644 }
1645 Ok(HandleResponse {
1646 messages: vec![],
1647 log: vec![],
1648 data: Some(to_binary(&HandleAnswer::ChangeAdmin { status: Success })?),
1649 })
1650}
1651
1652pub fn set_contract_status<S: Storage, A: Api, Q: Querier>(
1663 deps: &mut Extern<S, A, Q>,
1664 env: Env,
1665 config: &mut Config,
1666 level: ContractStatus,
1667) -> HandleResult {
1668 let sender_raw = deps.api.canonical_address(&env.message.sender)?;
1669 if config.admin != sender_raw {
1670 return Err(StdError::generic_err(
1671 "This is an admin command and can only be run from the admin address",
1672 ));
1673 }
1674 let new_status = level.to_u8();
1675 if config.status != new_status {
1676 config.status = new_status;
1677 save(&mut deps.storage, CONFIG_KEY, &config)?;
1678 }
1679 Ok(HandleResponse {
1680 messages: vec![],
1681 log: vec![],
1682 data: Some(to_binary(&HandleAnswer::SetContractStatus {
1683 status: Success,
1684 })?),
1685 })
1686}
1687
1688fn revoke_permit<S: Storage>(
1698 storage: &mut S,
1699 sender: &HumanAddr,
1700 permit_name: &str,
1701) -> HandleResult {
1702 RevokedPermits::revoke_permit(storage, PREFIX_REVOKED_PERMITS, sender, permit_name);
1703
1704 Ok(HandleResponse {
1705 messages: vec![],
1706 log: vec![],
1707 data: Some(to_binary(&HandleAnswer::RevokePermit { status: Success })?),
1708 })
1709}
1710
1711pub fn query<S: Storage, A: Api, Q: Querier>(deps: &Extern<S, A, Q>, msg: QueryMsg) -> QueryResult {
1719 let response = match msg {
1720 QueryMsg::ContractInfo {} => query_contract_info(&deps.storage),
1721 QueryMsg::ContractCreator {} => query_contract_creator(deps),
1722 QueryMsg::RoyaltyInfo { token_id, viewer } => {
1723 query_royalty(deps, token_id.as_deref(), viewer, None)
1724 }
1725 QueryMsg::ContractConfig {} => query_config(&deps.storage),
1726 QueryMsg::Minters {} => query_minters(deps),
1727 QueryMsg::NumTokens { viewer } => query_num_tokens(deps, viewer, None),
1728 QueryMsg::AllTokens {
1729 viewer,
1730 start_after,
1731 limit,
1732 } => query_all_tokens(deps, viewer, start_after, limit, None),
1733 QueryMsg::OwnerOf {
1734 token_id,
1735 viewer,
1736 include_expired,
1737 } => query_owner_of(deps, &token_id, viewer, include_expired, None),
1738 QueryMsg::NftInfo { token_id } => query_nft_info(&deps.storage, &token_id),
1739 QueryMsg::PrivateMetadata { token_id, viewer } => {
1740 query_private_meta(deps, &token_id, viewer, None)
1741 }
1742 QueryMsg::AllNftInfo {
1743 token_id,
1744 viewer,
1745 include_expired,
1746 } => query_all_nft_info(deps, &token_id, viewer, include_expired, None),
1747 QueryMsg::NftDossier {
1748 token_id,
1749 viewer,
1750 include_expired,
1751 } => query_nft_dossier(deps, token_id, viewer, include_expired, None),
1752 QueryMsg::BatchNftDossier {
1753 token_ids,
1754 viewer,
1755 include_expired,
1756 } => query_batch_nft_dossier(deps, token_ids, viewer, include_expired, None),
1757 QueryMsg::TokenApprovals {
1758 token_id,
1759 viewing_key,
1760 include_expired,
1761 } => query_token_approvals(deps, &token_id, Some(viewing_key), include_expired, None),
1762 QueryMsg::InventoryApprovals {
1763 address,
1764 viewing_key,
1765 include_expired,
1766 } => {
1767 let viewer = Some(ViewerInfo {
1768 address,
1769 viewing_key,
1770 });
1771 query_inventory_approvals(deps, viewer, include_expired, None)
1772 }
1773 QueryMsg::ApprovedForAll {
1774 owner,
1775 viewing_key,
1776 include_expired,
1777 } => query_approved_for_all(deps, Some(&owner), viewing_key, include_expired, None),
1778 QueryMsg::Tokens {
1779 owner,
1780 viewer,
1781 viewing_key,
1782 start_after,
1783 limit,
1784 } => query_tokens(deps, &owner, viewer, viewing_key, start_after, limit, None),
1785 QueryMsg::NumTokensOfOwner {
1786 owner,
1787 viewer,
1788 viewing_key,
1789 } => query_num_owner_tokens(deps, &owner, viewer, viewing_key, None),
1790 QueryMsg::VerifyTransferApproval {
1791 token_ids,
1792 address,
1793 viewing_key,
1794 } => {
1795 let viewer = Some(ViewerInfo {
1796 address,
1797 viewing_key,
1798 });
1799 query_verify_approval(deps, token_ids, viewer, None)
1800 }
1801 QueryMsg::IsUnwrapped { token_id } => query_is_unwrapped(&deps.storage, &token_id),
1802 QueryMsg::IsTransferable { token_id } => query_is_transferable(&deps.storage, &token_id),
1803 QueryMsg::ImplementsNonTransferableTokens {} => {
1804 to_binary(&QueryAnswer::ImplementsNonTransferableTokens { is_enabled: true })
1805 }
1806 QueryMsg::ImplementsTokenSubtype {} => {
1807 to_binary(&QueryAnswer::ImplementsTokenSubtype { is_enabled: true })
1808 }
1809 QueryMsg::TransactionHistory {
1810 address,
1811 viewing_key,
1812 page,
1813 page_size,
1814 } => {
1815 let viewer = Some(ViewerInfo {
1816 address,
1817 viewing_key,
1818 });
1819 query_transactions(deps, viewer, page, page_size, None)
1820 }
1821 QueryMsg::RegisteredCodeHash { contract } => query_code_hash(deps, &contract),
1822 QueryMsg::WithPermit { permit, query } => permit_queries(deps, permit, query),
1823 };
1824 pad_query_result(response, BLOCK_SIZE)
1825}
1826
1827pub fn permit_queries<S: Storage, A: Api, Q: Querier>(
1836 deps: &Extern<S, A, Q>,
1837 permit: Permit,
1838 query: QueryWithPermit,
1839) -> QueryResult {
1840 let my_address = deps
1842 .api
1843 .human_address(&load::<CanonicalAddr, _>(&deps.storage, MY_ADDRESS_KEY)?)?;
1844 let querier = deps.api.canonical_address(&HumanAddr(validate(
1845 deps,
1846 PREFIX_REVOKED_PERMITS,
1847 &permit,
1848 my_address,
1849 Some("secret"),
1850 )?))?;
1851 if !permit.check_permission(&TokenPermissions::Owner) {
1852 return Err(StdError::generic_err(format!(
1853 "Owner permission is required for SNIP-721 queries, got permissions {:?}",
1854 permit.params.permissions
1855 )));
1856 }
1857 match query {
1859 QueryWithPermit::RoyaltyInfo { token_id } => {
1860 query_royalty(deps, token_id.as_deref(), None, Some(querier))
1861 }
1862 QueryWithPermit::PrivateMetadata { token_id } => {
1863 query_private_meta(deps, &token_id, None, Some(querier))
1864 }
1865 QueryWithPermit::NftDossier {
1866 token_id,
1867 include_expired,
1868 } => query_nft_dossier(deps, token_id, None, include_expired, Some(querier)),
1869 QueryWithPermit::BatchNftDossier {
1870 token_ids,
1871 include_expired,
1872 } => query_batch_nft_dossier(deps, token_ids, None, include_expired, Some(querier)),
1873 QueryWithPermit::OwnerOf {
1874 token_id,
1875 include_expired,
1876 } => query_owner_of(deps, &token_id, None, include_expired, Some(querier)),
1877 QueryWithPermit::AllNftInfo {
1878 token_id,
1879 include_expired,
1880 } => query_all_nft_info(deps, &token_id, None, include_expired, Some(querier)),
1881 QueryWithPermit::InventoryApprovals { include_expired } => {
1882 query_inventory_approvals(deps, None, include_expired, Some(querier))
1883 }
1884 QueryWithPermit::VerifyTransferApproval { token_ids } => {
1885 query_verify_approval(deps, token_ids, None, Some(querier))
1886 }
1887 QueryWithPermit::TransactionHistory { page, page_size } => {
1888 query_transactions(deps, None, page, page_size, Some(querier))
1889 }
1890 QueryWithPermit::NumTokens {} => query_num_tokens(deps, None, Some(querier)),
1891 QueryWithPermit::AllTokens { start_after, limit } => {
1892 query_all_tokens(deps, None, start_after, limit, Some(querier))
1893 }
1894 QueryWithPermit::TokenApprovals {
1895 token_id,
1896 include_expired,
1897 } => query_token_approvals(deps, &token_id, None, include_expired, Some(querier)),
1898 QueryWithPermit::ApprovedForAll { include_expired } => {
1899 query_approved_for_all(deps, None, None, include_expired, Some(querier))
1900 }
1901 QueryWithPermit::Tokens {
1902 owner,
1903 start_after,
1904 limit,
1905 } => query_tokens(deps, &owner, None, None, start_after, limit, Some(querier)),
1906 QueryWithPermit::NumTokensOfOwner { owner } => {
1907 query_num_owner_tokens(deps, &owner, None, None, Some(querier))
1908 }
1909 }
1910}
1911
1912pub fn query_contract_creator<S: Storage, A: Api, Q: Querier>(
1918 deps: &Extern<S, A, Q>,
1919) -> QueryResult {
1920 let creator_raw: CanonicalAddr = load(&deps.storage, CREATOR_KEY)?;
1921 to_binary(&QueryAnswer::ContractCreator {
1922 creator: Some(deps.api.human_address(&creator_raw)?),
1923 })
1924}
1925
1926pub fn query_contract_info<S: ReadonlyStorage>(storage: &S) -> QueryResult {
1932 let config: Config = load(storage, CONFIG_KEY)?;
1933
1934 to_binary(&QueryAnswer::ContractInfo {
1935 name: config.name,
1936 symbol: config.symbol,
1937 })
1938}
1939
1940pub fn query_royalty<S: Storage, A: Api, Q: Querier>(
1950 deps: &Extern<S, A, Q>,
1951 token_id: Option<&str>,
1952 viewer: Option<ViewerInfo>,
1953 from_permit: Option<CanonicalAddr>,
1954) -> QueryResult {
1955 let viewer_raw = get_querier(deps, viewer, from_permit)?;
1956 let (royalty, hide_addr) = if let Some(id) = token_id {
1957 let block: BlockInfo = may_load(&deps.storage, BLOCK_KEY)?.unwrap_or_else(|| BlockInfo {
1959 height: 1,
1960 time: 1,
1961 chain_id: "not used".to_string(),
1962 });
1963 if let Ok((token, idx)) = get_token(&deps.storage, id, None) {
1965 let hide_addr = check_perm_core(
1966 deps,
1967 &block,
1968 &token,
1969 id,
1970 viewer_raw.as_ref(),
1971 token.owner.as_slice(),
1972 PermissionType::Transfer.to_usize(),
1973 &mut Vec::new(),
1974 "",
1975 )
1976 .is_err();
1977 let roy_store = ReadonlyPrefixedStorage::new(PREFIX_ROYALTY_INFO, &deps.storage);
1979 (
1980 may_load::<StoredRoyaltyInfo, _>(&roy_store, &idx.to_le_bytes())?,
1981 hide_addr,
1982 )
1983 } else {
1985 let config: Config = load(&deps.storage, CONFIG_KEY)?;
1986 let minters: Vec<CanonicalAddr> =
1987 may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
1988 let is_minter = viewer_raw.map(|v| minters.contains(&v)).unwrap_or(false);
1989 if config.token_supply_is_public || is_minter {
1991 return Err(StdError::generic_err(format!("Token ID: {} not found", id)));
1992 }
1993 (
1995 may_load::<StoredRoyaltyInfo, _>(&deps.storage, DEFAULT_ROYALTY_KEY)?,
1996 true,
1997 )
1998 }
1999 } else {
2001 let minters: Vec<CanonicalAddr> = may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
2002 (
2004 may_load::<StoredRoyaltyInfo, _>(&deps.storage, DEFAULT_ROYALTY_KEY)?,
2005 viewer_raw.map(|v| !minters.contains(&v)).unwrap_or(true),
2006 )
2007 };
2008 to_binary(&QueryAnswer::RoyaltyInfo {
2009 royalty_info: royalty
2010 .map(|s| s.to_human(&deps.api, hide_addr))
2011 .transpose()?,
2012 })
2013}
2014
2015pub fn query_config<S: ReadonlyStorage>(storage: &S) -> QueryResult {
2021 let config: Config = load(storage, CONFIG_KEY)?;
2022
2023 to_binary(&QueryAnswer::ContractConfig {
2024 token_supply_is_public: config.token_supply_is_public,
2025 owner_is_public: config.owner_is_public,
2026 sealed_metadata_is_enabled: config.sealed_metadata_is_enabled,
2027 unwrapped_metadata_is_private: config.unwrap_to_private,
2028 minter_may_update_metadata: config.minter_may_update_metadata,
2029 owner_may_update_metadata: config.owner_may_update_metadata,
2030 burn_is_enabled: config.burn_is_enabled,
2031 })
2032}
2033
2034pub fn query_minters<S: Storage, A: Api, Q: Querier>(deps: &Extern<S, A, Q>) -> QueryResult {
2040 let minters: Vec<CanonicalAddr> = may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
2041
2042 to_binary(&QueryAnswer::Minters {
2043 minters: minters
2044 .iter()
2045 .map(|m| deps.api.human_address(m))
2046 .collect::<StdResult<Vec<HumanAddr>>>()?,
2047 })
2048}
2049
2050pub fn query_num_tokens<S: Storage, A: Api, Q: Querier>(
2058 deps: &Extern<S, A, Q>,
2059 viewer: Option<ViewerInfo>,
2060 from_permit: Option<CanonicalAddr>,
2061) -> QueryResult {
2062 check_view_supply(deps, viewer, from_permit)?;
2064 let config: Config = load(&deps.storage, CONFIG_KEY)?;
2065 to_binary(&QueryAnswer::NumTokens {
2066 count: config.token_cnt,
2067 })
2068}
2069
2070pub fn query_all_tokens<S: Storage, A: Api, Q: Querier>(
2080 deps: &Extern<S, A, Q>,
2081 viewer: Option<ViewerInfo>,
2082 start_after: Option<String>,
2083 limit: Option<u32>,
2084 from_permit: Option<CanonicalAddr>,
2085) -> QueryResult {
2086 check_view_supply(deps, viewer, from_permit)?;
2088 let mut i = start_after.map_or_else(
2089 || Ok(0),
2090 |id| {
2091 let map2idx = ReadonlyPrefixedStorage::new(PREFIX_MAP_TO_INDEX, &deps.storage);
2092 let idx: u32 = may_load(&map2idx, id.as_bytes())?
2093 .ok_or_else(|| StdError::generic_err(format!("Token ID: {} not found", id)))?;
2094 idx.checked_add(1).ok_or_else(|| {
2095 StdError::generic_err("This token was the last one the contract could mint")
2096 })
2097 },
2098 )?;
2099 let cut_off = limit.unwrap_or(300);
2100 let config: Config = load(&deps.storage, CONFIG_KEY)?;
2101 let mut tokens = Vec::new();
2102 let mut count = 0u32;
2103 let map2id = ReadonlyPrefixedStorage::new(PREFIX_MAP_TO_ID, &deps.storage);
2104 while count < cut_off && i < config.mint_cnt {
2105 if let Some(id) = may_load::<String, _>(&map2id, &i.to_le_bytes())? {
2106 tokens.push(id);
2107 count += 1;
2109 }
2110 i += 1;
2112 }
2113 to_binary(&QueryAnswer::TokenList { tokens })
2114}
2115
2116pub fn query_owner_of<S: Storage, A: Api, Q: Querier>(
2127 deps: &Extern<S, A, Q>,
2128 token_id: &str,
2129 viewer: Option<ViewerInfo>,
2130 include_expired: Option<bool>,
2131 from_permit: Option<CanonicalAddr>,
2132) -> QueryResult {
2133 let (may_owner, approvals, _idx) =
2134 process_cw721_owner_of(deps, token_id, viewer, include_expired, from_permit)?;
2135 if let Some(owner) = may_owner {
2136 return to_binary(&QueryAnswer::OwnerOf { owner, approvals });
2137 }
2138 Err(StdError::generic_err(format!(
2139 "You are not authorized to view the owner of token {}",
2140 token_id
2141 )))
2142}
2143
2144pub fn query_nft_info<S: ReadonlyStorage>(storage: &S, token_id: &str) -> QueryResult {
2151 let map2idx = ReadonlyPrefixedStorage::new(PREFIX_MAP_TO_INDEX, storage);
2152 let may_idx: Option<u32> = may_load(&map2idx, token_id.as_bytes())?;
2153 if let Some(idx) = may_idx {
2155 let meta_store = ReadonlyPrefixedStorage::new(PREFIX_PUB_META, storage);
2156 let meta: Metadata = may_load(&meta_store, &idx.to_le_bytes())?.unwrap_or(Metadata {
2157 token_uri: None,
2158 extension: None,
2159 });
2160 return to_binary(&QueryAnswer::NftInfo {
2161 token_uri: meta.token_uri,
2162 extension: meta.extension,
2163 });
2164 }
2165 let config: Config = load(storage, CONFIG_KEY)?;
2166 if config.token_supply_is_public {
2169 return Err(StdError::generic_err(format!(
2170 "Token ID: {} not found",
2171 token_id
2172 )));
2173 }
2174 to_binary(&QueryAnswer::NftInfo {
2176 token_uri: None,
2177 extension: None,
2178 })
2179}
2180
2181pub fn query_private_meta<S: Storage, A: Api, Q: Querier>(
2191 deps: &Extern<S, A, Q>,
2192 token_id: &str,
2193 viewer: Option<ViewerInfo>,
2194 from_permit: Option<CanonicalAddr>,
2195) -> QueryResult {
2196 let prep_info = query_token_prep(deps, token_id, viewer, from_permit)?;
2197 check_perm_core(
2198 deps,
2199 &prep_info.block,
2200 &prep_info.token,
2201 token_id,
2202 prep_info.viewer_raw.as_ref(),
2203 prep_info.token.owner.as_slice(),
2204 PermissionType::ViewMetadata.to_usize(),
2205 &mut Vec::new(),
2206 &prep_info.err_msg,
2207 )?;
2208 if !prep_info.token.unwrapped {
2210 return Err(StdError::generic_err(
2211 "Sealed metadata must be unwrapped by calling Reveal before it can be viewed",
2212 ));
2213 }
2214 let meta_store = ReadonlyPrefixedStorage::new(PREFIX_PRIV_META, &deps.storage);
2215 let meta: Metadata = may_load(&meta_store, &prep_info.idx.to_le_bytes())?.unwrap_or(Metadata {
2216 token_uri: None,
2217 extension: None,
2218 });
2219 to_binary(&QueryAnswer::PrivateMetadata {
2220 token_uri: meta.token_uri,
2221 extension: meta.extension,
2222 })
2223}
2224
2225pub fn query_all_nft_info<S: Storage, A: Api, Q: Querier>(
2235 deps: &Extern<S, A, Q>,
2236 token_id: &str,
2237 viewer: Option<ViewerInfo>,
2238 include_expired: Option<bool>,
2239 from_permit: Option<CanonicalAddr>,
2240) -> QueryResult {
2241 let (owner, approvals, idx) =
2242 process_cw721_owner_of(deps, token_id, viewer, include_expired, from_permit)?;
2243 let meta_store = ReadonlyPrefixedStorage::new(PREFIX_PUB_META, &deps.storage);
2244 let info: Option<Metadata> = may_load(&meta_store, &idx.to_le_bytes())?;
2245 let access = Cw721OwnerOfResponse { owner, approvals };
2246 to_binary(&QueryAnswer::AllNftInfo { access, info })
2247}
2248
2249pub fn query_nft_dossier<S: Storage, A: Api, Q: Querier>(
2262 deps: &Extern<S, A, Q>,
2263 token_id: String,
2264 viewer: Option<ViewerInfo>,
2265 include_expired: Option<bool>,
2266 from_permit: Option<CanonicalAddr>,
2267) -> QueryResult {
2268 let dossier = dossier_list(deps, vec![token_id], viewer, include_expired, from_permit)?
2269 .pop()
2270 .ok_or_else(|| {
2271 StdError::generic_err("NftDossier can never return an empty dossier list")
2272 })?;
2273
2274 to_binary(&QueryAnswer::NftDossier {
2275 owner: dossier.owner,
2276 public_metadata: dossier.public_metadata,
2277 private_metadata: dossier.private_metadata,
2278 royalty_info: dossier.royalty_info,
2279 mint_run_info: dossier.mint_run_info,
2280 transferable: dossier.transferable,
2281 unwrapped: dossier.unwrapped,
2282 display_private_metadata_error: dossier.display_private_metadata_error,
2283 owner_is_public: dossier.owner_is_public,
2284 public_ownership_expiration: dossier.public_ownership_expiration,
2285 private_metadata_is_public: dossier.private_metadata_is_public,
2286 private_metadata_is_public_expiration: dossier.private_metadata_is_public_expiration,
2287 token_approvals: dossier.token_approvals,
2288 inventory_approvals: dossier.inventory_approvals,
2289 })
2290}
2291
2292pub fn query_batch_nft_dossier<S: Storage, A: Api, Q: Querier>(
2305 deps: &Extern<S, A, Q>,
2306 token_ids: Vec<String>,
2307 viewer: Option<ViewerInfo>,
2308 include_expired: Option<bool>,
2309 from_permit: Option<CanonicalAddr>,
2310) -> QueryResult {
2311 let nft_dossiers = dossier_list(deps, token_ids, viewer, include_expired, from_permit)?;
2312
2313 to_binary(&QueryAnswer::BatchNftDossier { nft_dossiers })
2314}
2315
2316pub fn query_token_approvals<S: Storage, A: Api, Q: Querier>(
2326 deps: &Extern<S, A, Q>,
2327 token_id: &str,
2328 viewing_key: Option<String>,
2329 include_expired: Option<bool>,
2330 from_permit: Option<CanonicalAddr>,
2331) -> QueryResult {
2332 let config: Config = load(&deps.storage, CONFIG_KEY)?;
2333 let custom_err = format!(
2334 "You are not authorized to view approvals for token {}",
2335 token_id
2336 );
2337 let opt_err = if config.token_supply_is_public {
2340 None
2341 } else {
2342 Some(&*custom_err)
2343 };
2344 let (mut token, _idx) = get_token(&deps.storage, token_id, opt_err)?;
2345 if let Some(pmt) = from_permit {
2347 if pmt != token.owner {
2348 return Err(StdError::generic_err(custom_err));
2349 }
2350 } else {
2351 let key = viewing_key.ok_or_else(|| {
2352 StdError::generic_err("This is being called incorrectly if there is no viewing key")
2353 })?;
2354 check_key(&deps.storage, &token.owner, key)?;
2355 }
2356 let owner_slice = token.owner.as_slice();
2357 let own_priv_store = ReadonlyPrefixedStorage::new(PREFIX_OWNER_PRIV, &deps.storage);
2358 let global_pass: bool =
2359 may_load(&own_priv_store, owner_slice)?.unwrap_or(config.owner_is_public);
2360 let block: BlockInfo = may_load(&deps.storage, BLOCK_KEY)?.unwrap_or_else(|| BlockInfo {
2362 height: 1,
2363 time: 1,
2364 chain_id: "not used".to_string(),
2365 });
2366 let perm_type_info = PermissionTypeInfo {
2367 view_owner_idx: PermissionType::ViewOwner.to_usize(),
2368 view_meta_idx: PermissionType::ViewMetadata.to_usize(),
2369 transfer_idx: PermissionType::Transfer.to_usize(),
2370 num_types: PermissionType::Transfer.num_types(),
2371 };
2372 let incl_exp = include_expired.unwrap_or(false);
2373 let (token_approvals, token_owner_exp, token_meta_exp) = gen_snip721_approvals(
2374 &deps.api,
2375 &block,
2376 &mut token.permissions,
2377 incl_exp,
2378 &perm_type_info,
2379 )?;
2380 let all_store = ReadonlyPrefixedStorage::new(PREFIX_ALL_PERMISSIONS, &deps.storage);
2381 let mut all_perm: Vec<Permission> = json_may_load(&all_store, owner_slice)?.unwrap_or_default();
2382 let (_inventory_approv, all_owner_exp, all_meta_exp) =
2383 gen_snip721_approvals(&deps.api, &block, &mut all_perm, incl_exp, &perm_type_info)?;
2384 let (public_ownership_expiration, owner_is_public) = if global_pass {
2386 (Some(Expiration::Never), true)
2387 } else if token_owner_exp.is_some() {
2388 (token_owner_exp, true)
2389 } else {
2390 (all_owner_exp, all_owner_exp.is_some())
2391 };
2392 let (private_metadata_is_public_expiration, private_metadata_is_public) =
2394 if token_meta_exp.is_some() {
2395 (token_meta_exp, true)
2396 } else {
2397 (all_meta_exp, all_meta_exp.is_some())
2398 };
2399 to_binary(&QueryAnswer::TokenApprovals {
2400 owner_is_public,
2401 public_ownership_expiration,
2402 private_metadata_is_public,
2403 private_metadata_is_public_expiration,
2404 token_approvals,
2405 })
2406}
2407
2408pub fn query_inventory_approvals<S: Storage, A: Api, Q: Querier>(
2417 deps: &Extern<S, A, Q>,
2418 viewer: Option<ViewerInfo>,
2419 include_expired: Option<bool>,
2420 from_permit: Option<CanonicalAddr>,
2421) -> QueryResult {
2422 let owner_raw = get_querier(deps, viewer, from_permit)?.ok_or_else(|| {
2423 StdError::generic_err("This is being called incorrectly if there is no querier address")
2424 })?;
2425 let owner_slice = owner_raw.as_slice();
2426 let own_priv_store = ReadonlyPrefixedStorage::new(PREFIX_OWNER_PRIV, &deps.storage);
2427 let config: Config = load(&deps.storage, CONFIG_KEY)?;
2428 let global_pass: bool =
2429 may_load(&own_priv_store, owner_slice)?.unwrap_or(config.owner_is_public);
2430 let block: BlockInfo = may_load(&deps.storage, BLOCK_KEY)?.unwrap_or_else(|| BlockInfo {
2432 height: 1,
2433 time: 1,
2434 chain_id: "not used".to_string(),
2435 });
2436 let all_store = ReadonlyPrefixedStorage::new(PREFIX_ALL_PERMISSIONS, &deps.storage);
2437 let mut all_perm: Vec<Permission> = json_may_load(&all_store, owner_slice)?.unwrap_or_default();
2438 let perm_type_info = PermissionTypeInfo {
2439 view_owner_idx: PermissionType::ViewOwner.to_usize(),
2440 view_meta_idx: PermissionType::ViewMetadata.to_usize(),
2441 transfer_idx: PermissionType::Transfer.to_usize(),
2442 num_types: PermissionType::Transfer.num_types(),
2443 };
2444 let (
2445 inventory_approvals,
2446 mut public_ownership_expiration,
2447 private_metadata_is_public_expiration,
2448 ) = gen_snip721_approvals(
2449 &deps.api,
2450 &block,
2451 &mut all_perm,
2452 include_expired.unwrap_or(false),
2453 &perm_type_info,
2454 )?;
2455 let owner_is_public = if global_pass {
2456 public_ownership_expiration = Some(Expiration::Never);
2457 true
2458 } else {
2459 public_ownership_expiration.is_some()
2460 };
2461 let private_metadata_is_public = private_metadata_is_public_expiration.is_some();
2462 to_binary(&QueryAnswer::InventoryApprovals {
2463 owner_is_public,
2464 public_ownership_expiration,
2465 private_metadata_is_public,
2466 private_metadata_is_public_expiration,
2467 inventory_approvals,
2468 })
2469}
2470
2471pub fn query_approved_for_all<S: Storage, A: Api, Q: Querier>(
2482 deps: &Extern<S, A, Q>,
2483 owner: Option<&HumanAddr>,
2484 viewing_key: Option<String>,
2485 include_expired: Option<bool>,
2486 from_permit: Option<CanonicalAddr>,
2487) -> QueryResult {
2488 let owner_raw = if let Some(pmt) = from_permit {
2490 pmt
2491 } else {
2492 let raw = deps.api.canonical_address(owner.ok_or_else(|| {
2493 StdError::generic_err("This is being called incorrectly if there is no owner address")
2494 })?)?;
2495 if let Some(key) = viewing_key {
2496 check_key(&deps.storage, &raw, key)?;
2497 } else {
2499 return to_binary(&QueryAnswer::ApprovedForAll {
2500 operators: Vec::new(),
2501 });
2502 }
2503 raw
2504 };
2505 let block: BlockInfo = may_load(&deps.storage, BLOCK_KEY)?.unwrap_or_else(|| BlockInfo {
2507 height: 1,
2508 time: 1,
2509 chain_id: "not used".to_string(),
2510 });
2511 let mut operators: Vec<Cw721Approval> = Vec::new();
2512 let all_store = ReadonlyPrefixedStorage::new(PREFIX_ALL_PERMISSIONS, &deps.storage);
2513 let all_perm: Vec<Permission> =
2514 json_may_load(&all_store, owner_raw.as_slice())?.unwrap_or_default();
2515 gen_cw721_approvals(
2516 &deps.api,
2517 &block,
2518 &all_perm,
2519 &mut operators,
2520 PermissionType::Transfer.to_usize(),
2521 include_expired.unwrap_or(false),
2522 )?;
2523
2524 to_binary(&QueryAnswer::ApprovedForAll { operators })
2525}
2526
2527pub fn query_tokens<S: Storage, A: Api, Q: Querier>(
2542 deps: &Extern<S, A, Q>,
2543 owner: &HumanAddr,
2544 viewer: Option<HumanAddr>,
2545 viewing_key: Option<String>,
2546 start_after: Option<String>,
2547 limit: Option<u32>,
2548 from_permit: Option<CanonicalAddr>,
2549) -> QueryResult {
2550 let owner_raw = deps.api.canonical_address(owner)?;
2551 let cut_off = limit.unwrap_or(30);
2552 let (is_owner, may_querier) = if let Some(pmt) = from_permit.as_ref() {
2554 (owner_raw == *pmt, from_permit)
2556 } else if let Some(key) = viewing_key {
2558 viewer
2560 .map(|v| deps.api.canonical_address(&v))
2562 .transpose()?
2563 .filter(|v| check_key(&deps.storage, v, key.clone()).is_ok())
2565 .map_or_else(
2566 || {
2568 check_key(&deps.storage, &owner_raw, key)?;
2570 Ok((true, Some(owner_raw.clone())))
2571 },
2572 |v| Ok((v == owner_raw, Some(v))),
2574 )?
2575 } else {
2577 (false, None)
2578 };
2579 if cut_off == 0 {
2581 return to_binary(&QueryAnswer::TokenList { tokens: Vec::new() });
2582 }
2583 let own_inv = Inventory::new(&deps.storage, owner_raw)?;
2585 let owner_slice = own_inv.owner.as_slice();
2586
2587 let querier = may_querier.as_ref();
2588 let mut may_config: Option<Config> = None;
2590 let mut known_pass = if !is_owner {
2591 let config: Config = load(&deps.storage, CONFIG_KEY)?;
2592 let own_priv_store = ReadonlyPrefixedStorage::new(PREFIX_OWNER_PRIV, &deps.storage);
2593 let pass: bool = may_load(&own_priv_store, owner_slice)?.unwrap_or(config.owner_is_public);
2594 may_config = Some(config);
2595 pass
2596 } else {
2597 true
2598 };
2599 let block = if !known_pass {
2601 let b: BlockInfo = may_load(&deps.storage, BLOCK_KEY)?.unwrap_or_else(|| BlockInfo {
2602 height: 1,
2603 time: 1,
2604 chain_id: "not used".to_string(),
2605 });
2606 b
2607 } else {
2608 BlockInfo {
2609 height: 1,
2610 time: 1,
2611 chain_id: "not used".to_string(),
2612 }
2613 };
2614 let exp_idx = PermissionType::ViewOwner.to_usize();
2615 let info_store = ReadonlyPrefixedStorage::new(PREFIX_INFOS, &deps.storage);
2616 let mut list_it: bool;
2617 let mut oper_for: Vec<CanonicalAddr> = Vec::new();
2618 let mut tokens: Vec<String> = Vec::new();
2619 let map2id = ReadonlyPrefixedStorage::new(PREFIX_MAP_TO_ID, &deps.storage);
2620 let mut inv_iter = if let Some(after) = start_after.as_ref() {
2621 let config = may_config.map_or_else(|| load::<Config, _>(&deps.storage, CONFIG_KEY), Ok)?;
2623 let inv_err = format!("Token ID: {} is not in the specified inventory", after);
2626 let unauth_err = format!(
2628 "You are not authorized to perform this action on token {}",
2629 after
2630 );
2631 let public_err = format!("Token ID: {} not found", after);
2632 let not_found_err = if config.token_supply_is_public {
2634 &public_err
2635 } else if known_pass {
2636 &inv_err
2637 } else {
2638 &unauth_err
2639 };
2640 let map2idx = ReadonlyPrefixedStorage::new(PREFIX_MAP_TO_INDEX, &deps.storage);
2641 let idx: u32 = may_load(&map2idx, after.as_bytes())?
2642 .ok_or_else(|| StdError::generic_err(not_found_err))?;
2643 if !known_pass {
2645 let token: Token = json_may_load(&info_store, &idx.to_le_bytes())?
2646 .ok_or_else(|| StdError::generic_err("Token info storage is corrupt"))?;
2647 let mut may_oper_vec = if own_inv.owner == token.owner {
2649 None
2650 } else {
2651 Some(Vec::new())
2652 };
2653 check_perm_core(
2654 deps,
2655 &block,
2656 &token,
2657 after,
2658 querier,
2659 token.owner.as_slice(),
2660 exp_idx,
2661 may_oper_vec.as_mut().unwrap_or(&mut oper_for),
2662 &unauth_err,
2663 )?;
2664 if !oper_for.is_empty() {
2666 known_pass = true;
2667 }
2668 }
2669 InventoryIter::start_after(&deps.storage, &own_inv, idx, &inv_err)?
2670 } else {
2671 InventoryIter::new(&own_inv)
2672 };
2673 let mut count = 0u32;
2674 while let Some(idx) = inv_iter.next(&deps.storage)? {
2675 if let Some(id) = may_load::<String, _>(&map2id, &idx.to_le_bytes())? {
2676 list_it = known_pass;
2677 if !known_pass {
2679 if let Some(token) = json_may_load::<Token, _>(&info_store, &idx.to_le_bytes())? {
2680 list_it = check_perm_core(
2681 deps,
2682 &block,
2683 &token,
2684 &id,
2685 querier,
2686 owner_slice,
2687 exp_idx,
2688 &mut oper_for,
2689 "",
2690 )
2691 .is_ok();
2692 if !oper_for.is_empty() {
2694 known_pass = true;
2695 }
2696 }
2697 }
2698 if list_it {
2699 tokens.push(id);
2700 count += 1;
2702 if count >= cut_off {
2704 break;
2705 }
2706 }
2707 }
2708 }
2709 to_binary(&QueryAnswer::TokenList { tokens })
2710}
2711
2712pub fn query_num_owner_tokens<S: Storage, A: Api, Q: Querier>(
2723 deps: &Extern<S, A, Q>,
2724 owner: &HumanAddr,
2725 viewer: Option<HumanAddr>,
2726 viewing_key: Option<String>,
2727 from_permit: Option<CanonicalAddr>,
2728) -> QueryResult {
2729 let owner_raw = deps.api.canonical_address(owner)?;
2730 let (is_owner, may_querier) = if let Some(pmt) = from_permit.as_ref() {
2732 (owner_raw == *pmt, from_permit)
2734 } else if let Some(key) = viewing_key {
2736 viewer
2738 .map(|v| deps.api.canonical_address(&v))
2740 .transpose()?
2741 .filter(|v| check_key(&deps.storage, v, key.clone()).is_ok())
2743 .map_or_else(
2744 || {
2746 check_key(&deps.storage, &owner_raw, key)?;
2748 Ok((true, Some(owner_raw.clone())))
2749 },
2750 |v| Ok((v == owner_raw, Some(v))),
2752 )?
2753 } else {
2755 (false, None)
2756 };
2757
2758 let own_inv = Inventory::new(&deps.storage, owner_raw)?;
2760 let owner_slice = own_inv.owner.as_slice();
2761
2762 let mut known_pass = if !is_owner {
2764 let config: Config = load(&deps.storage, CONFIG_KEY)?;
2765 let own_priv_store = ReadonlyPrefixedStorage::new(PREFIX_OWNER_PRIV, &deps.storage);
2766 let pass: bool = may_load(&own_priv_store, owner_slice)?.unwrap_or(config.owner_is_public);
2767 pass
2768 } else {
2769 true
2770 };
2771 let block = if !known_pass {
2773 let b: BlockInfo = may_load(&deps.storage, BLOCK_KEY)?.unwrap_or_else(|| BlockInfo {
2774 height: 1,
2775 time: 1,
2776 chain_id: "not used".to_string(),
2777 });
2778 b
2779 } else {
2780 BlockInfo {
2781 height: 1,
2782 time: 1,
2783 chain_id: "not used".to_string(),
2784 }
2785 };
2786 let exp_idx = PermissionType::ViewOwner.to_usize();
2787 let global_raw = CanonicalAddr(Binary::from(b"public"));
2788 let (sender, only_public) = if let Some(sdr) = may_querier.as_ref() {
2789 (sdr, false)
2790 } else {
2791 (&global_raw, true)
2792 };
2793 let mut found_one = only_public;
2794 if !known_pass {
2795 let all_store = ReadonlyPrefixedStorage::new(PREFIX_ALL_PERMISSIONS, &deps.storage);
2797 let may_list: Option<Vec<Permission>> = json_may_load(&all_store, owner_slice)?;
2798 if let Some(list) = may_list {
2799 for perm in &list {
2800 if perm.address == *sender || perm.address == global_raw {
2801 if let Some(exp) = perm.expirations[exp_idx] {
2802 if !exp.is_expired(&block) {
2803 known_pass = true;
2804 break;
2805 }
2806 }
2807 if found_one {
2809 break;
2810 } else {
2811 found_one = true;
2812 }
2813 }
2814 }
2815 }
2816 }
2817 if known_pass {
2820 return to_binary(&QueryAnswer::NumTokens {
2821 count: own_inv.info.count,
2822 });
2823 }
2824
2825 let mut token_idxs: HashSet<u32> = HashSet::new();
2827 found_one = only_public;
2828 let auth_store = ReadonlyPrefixedStorage::new(PREFIX_AUTHLIST, &deps.storage);
2829 let auth_list: Vec<AuthList> = may_load(&auth_store, owner_slice)?.unwrap_or_default();
2830 for auth in auth_list.iter() {
2831 if auth.address == *sender || auth.address == global_raw {
2832 token_idxs.extend(auth.tokens[exp_idx].iter());
2833 if found_one {
2834 break;
2835 } else {
2836 found_one = true;
2837 }
2838 }
2839 }
2840 let info_store = ReadonlyPrefixedStorage::new(PREFIX_INFOS, &deps.storage);
2842 let mut count = 0u32;
2843 for idx in token_idxs.into_iter() {
2844 if let Some(token) = json_may_load::<Token, _>(&info_store, &idx.to_le_bytes())? {
2845 found_one = only_public;
2846 for perm in token.permissions.iter() {
2847 if perm.address == *sender || perm.address == global_raw {
2848 if let Some(exp) = perm.expirations[exp_idx] {
2849 if !exp.is_expired(&block) {
2850 count += 1;
2851 break;
2852 }
2853 }
2854 if found_one {
2856 break;
2857 } else {
2858 found_one = true;
2859 }
2860 }
2861 }
2862 }
2863 }
2864
2865 to_binary(&QueryAnswer::NumTokens { count })
2866}
2867
2868pub fn query_is_unwrapped<S: ReadonlyStorage>(storage: &S, token_id: &str) -> QueryResult {
2875 let config: Config = load(storage, CONFIG_KEY)?;
2876 let get_token_res = get_token(storage, token_id, None);
2877 match get_token_res {
2878 Err(err) => match err {
2879 StdError::GenericErr { msg, .. }
2882 if !config.token_supply_is_public && msg.contains("Token ID") =>
2883 {
2884 to_binary(&QueryAnswer::IsUnwrapped {
2885 token_is_unwrapped: !config.sealed_metadata_is_enabled,
2886 })
2887 }
2888 _ => Err(err),
2889 },
2890 Ok((token, _idx)) => to_binary(&QueryAnswer::IsUnwrapped {
2891 token_is_unwrapped: token.unwrapped,
2892 }),
2893 }
2894}
2895
2896pub fn query_is_transferable<S: ReadonlyStorage>(storage: &S, token_id: &str) -> QueryResult {
2902 let config: Config = load(storage, CONFIG_KEY)?;
2903 let get_token_res = get_token(storage, token_id, None);
2904 match get_token_res {
2905 Err(err) => match err {
2906 StdError::GenericErr { msg, .. }
2909 if !config.token_supply_is_public && msg.contains("Token ID") =>
2910 {
2911 to_binary(&QueryAnswer::IsTransferable {
2912 token_is_transferable: true,
2913 })
2914 }
2915 _ => Err(err),
2916 },
2917 Ok((token, _idx)) => to_binary(&QueryAnswer::IsTransferable {
2918 token_is_transferable: token.transferable,
2919 }),
2920 }
2921}
2922
2923pub fn query_transactions<S: Storage, A: Api, Q: Querier>(
2935 deps: &Extern<S, A, Q>,
2936 viewer: Option<ViewerInfo>,
2937 page: Option<u32>,
2938 page_size: Option<u32>,
2939 from_permit: Option<CanonicalAddr>,
2940) -> StdResult<Binary> {
2941 let address_raw = get_querier(deps, viewer, from_permit)?.ok_or_else(|| {
2942 StdError::generic_err("This is being called incorrectly if there is no querier address")
2943 })?;
2944 let (txs, total) = get_txs(
2945 &deps.api,
2946 &deps.storage,
2947 &address_raw,
2948 page.unwrap_or(0),
2949 page_size.unwrap_or(30),
2950 )?;
2951 to_binary(&QueryAnswer::TransactionHistory { total, txs })
2952}
2953
2954pub fn query_verify_approval<S: Storage, A: Api, Q: Querier>(
2964 deps: &Extern<S, A, Q>,
2965 token_ids: Vec<String>,
2966 viewer: Option<ViewerInfo>,
2967 from_permit: Option<CanonicalAddr>,
2968) -> StdResult<Binary> {
2969 let address_raw = get_querier(deps, viewer, from_permit)?.ok_or_else(|| {
2970 StdError::generic_err("This is being called incorrectly if there is no querier address")
2971 })?;
2972 let config: Config = load(&deps.storage, CONFIG_KEY)?;
2973 let block: BlockInfo = may_load(&deps.storage, BLOCK_KEY)?.unwrap_or_else(|| BlockInfo {
2975 height: 1,
2976 time: 1,
2977 chain_id: "not used".to_string(),
2978 });
2979 let mut oper_for: Vec<CanonicalAddr> = Vec::new();
2980 for id in token_ids.into_iter() {
2981 #[allow(clippy::blocks_in_if_conditions)]
2983 if get_token_if_permitted(
2984 deps,
2985 &block,
2986 &id,
2987 Some(&address_raw),
2988 PermissionType::Transfer,
2989 &mut oper_for,
2990 &config,
2991 )
2993 .and_then(|(t, _)| {
2994 if t.transferable {
2995 Ok(())
2996 } else {
2997 Err(StdError::unauthorized())
2998 }
2999 })
3000 .is_err()
3001 {
3002 return to_binary(&QueryAnswer::VerifyTransferApproval {
3003 approved_for_all: false,
3004 first_unapproved_token: Some(id),
3005 });
3006 }
3007 }
3008 to_binary(&QueryAnswer::VerifyTransferApproval {
3009 approved_for_all: true,
3010 first_unapproved_token: None,
3011 })
3012}
3013
3014pub fn query_code_hash<S: Storage, A: Api, Q: Querier>(
3022 deps: &Extern<S, A, Q>,
3023 contract: &HumanAddr,
3024) -> QueryResult {
3025 let contract_raw = deps.api.canonical_address(contract)?;
3026 let store = ReadonlyPrefixedStorage::new(PREFIX_RECEIVERS, &deps.storage);
3027 let may_reg_rec: Option<ReceiveRegistration> = may_load(&store, contract_raw.as_slice())?;
3028 if let Some(reg_rec) = may_reg_rec {
3029 return to_binary(&QueryAnswer::RegisteredCodeHash {
3030 code_hash: Some(reg_rec.code_hash),
3031 also_implements_batch_receive_nft: reg_rec.impl_batch,
3032 });
3033 }
3034 to_binary(&QueryAnswer::RegisteredCodeHash {
3035 code_hash: None,
3036 also_implements_batch_receive_nft: false,
3037 })
3038}
3039
3040pub struct TokenQueryInfo {
3042 viewer_raw: Option<CanonicalAddr>,
3044 block: BlockInfo,
3046 err_msg: String,
3048 token: Token,
3050 idx: u32,
3052 owner_is_public: bool,
3054}
3055
3056fn query_token_prep<S: Storage, A: Api, Q: Querier>(
3066 deps: &Extern<S, A, Q>,
3067 token_id: &str,
3068 viewer: Option<ViewerInfo>,
3069 from_permit: Option<CanonicalAddr>,
3070) -> StdResult<TokenQueryInfo> {
3071 let viewer_raw = get_querier(deps, viewer, from_permit)?;
3072 let config: Config = load(&deps.storage, CONFIG_KEY)?;
3073 let block: BlockInfo = may_load(&deps.storage, BLOCK_KEY)?.unwrap_or_else(|| BlockInfo {
3075 height: 1,
3076 time: 1,
3077 chain_id: "not used".to_string(),
3078 });
3079 let err_msg = format!(
3080 "You are not authorized to perform this action on token {}",
3081 token_id
3082 );
3083 let opt_err = if config.token_supply_is_public {
3086 None
3087 } else {
3088 Some(&*err_msg)
3089 };
3090 let (token, idx) = get_token(&deps.storage, token_id, opt_err)?;
3091 Ok(TokenQueryInfo {
3092 viewer_raw,
3093 block,
3094 err_msg,
3095 token,
3096 idx,
3097 owner_is_public: config.owner_is_public,
3098 })
3099}
3100
3101fn process_cw721_owner_of<S: Storage, A: Api, Q: Querier>(
3112 deps: &Extern<S, A, Q>,
3113 token_id: &str,
3114 viewer: Option<ViewerInfo>,
3115 include_expired: Option<bool>,
3116 from_permit: Option<CanonicalAddr>,
3117) -> StdResult<(Option<HumanAddr>, Vec<Cw721Approval>, u32)> {
3118 let prep_info = query_token_prep(deps, token_id, viewer, from_permit)?;
3119 let opt_viewer = prep_info.viewer_raw.as_ref();
3120 if check_permission(
3121 deps,
3122 &prep_info.block,
3123 &prep_info.token,
3124 token_id,
3125 opt_viewer,
3126 PermissionType::ViewOwner,
3127 &mut Vec::new(),
3128 &prep_info.err_msg,
3129 prep_info.owner_is_public,
3130 )
3131 .is_ok()
3132 {
3133 let (owner, mut approvals, mut operators) = get_owner_of_resp(
3134 deps,
3135 &prep_info.block,
3136 &prep_info.token,
3137 opt_viewer,
3138 include_expired.unwrap_or(false),
3139 )?;
3140 approvals.append(&mut operators);
3141 return Ok((Some(owner), approvals, prep_info.idx));
3142 }
3143 Ok((None, Vec::new(), prep_info.idx))
3144}
3145
3146fn get_owner_of_resp<S: Storage, A: Api, Q: Querier>(
3158 deps: &Extern<S, A, Q>,
3159 block: &BlockInfo,
3160 token: &Token,
3161 viewer: Option<&CanonicalAddr>,
3162 include_expired: bool,
3163) -> StdResult<(HumanAddr, Vec<Cw721Approval>, Vec<Cw721Approval>)> {
3164 let owner = deps.api.human_address(&token.owner)?;
3165 let mut spenders: Vec<Cw721Approval> = Vec::new();
3166 let mut operators: Vec<Cw721Approval> = Vec::new();
3167 if let Some(vwr) = viewer {
3168 if token.owner == *vwr {
3169 let transfer_idx = PermissionType::Transfer.to_usize();
3170 gen_cw721_approvals(
3171 &deps.api,
3172 block,
3173 &token.permissions,
3174 &mut spenders,
3175 transfer_idx,
3176 include_expired,
3177 )?;
3178 let all_store = ReadonlyPrefixedStorage::new(PREFIX_ALL_PERMISSIONS, &deps.storage);
3179 let all_perm: Vec<Permission> =
3180 json_may_load(&all_store, token.owner.as_slice())?.unwrap_or_default();
3181 gen_cw721_approvals(
3182 &deps.api,
3183 block,
3184 &all_perm,
3185 &mut operators,
3186 transfer_idx,
3187 include_expired,
3188 )?;
3189 }
3190 }
3191 Ok((owner, spenders, operators))
3192}
3193
3194fn gen_cw721_approvals<A: Api>(
3206 api: &A,
3207 block: &BlockInfo,
3208 perm_list: &[Permission],
3209 approvals: &mut Vec<Cw721Approval>,
3210 transfer_idx: usize,
3211 include_expired: bool,
3212) -> StdResult<()> {
3213 let global_raw = CanonicalAddr(Binary::from(b"public"));
3214 for perm in perm_list {
3215 if let Some(exp) = perm.expirations[transfer_idx] {
3216 if (include_expired || !exp.is_expired(block)) && perm.address != global_raw {
3217 approvals.push(Cw721Approval {
3218 spender: api.human_address(&perm.address)?,
3219 expires: exp,
3220 });
3221 }
3222 }
3223 }
3224 Ok(())
3225}
3226
3227pub struct PermissionTypeInfo {
3229 pub view_owner_idx: usize,
3231 pub view_meta_idx: usize,
3233 pub transfer_idx: usize,
3235 pub num_types: usize,
3237}
3238
3239fn gen_snip721_approvals<A: Api>(
3251 api: &A,
3252 block: &BlockInfo,
3253 perm_list: &mut Vec<Permission>,
3254 include_expired: bool,
3255 perm_type_info: &PermissionTypeInfo,
3256) -> StdResult<(Vec<Snip721Approval>, Option<Expiration>, Option<Expiration>)> {
3257 let global_raw = CanonicalAddr(Binary::from(b"public"));
3258 let mut approvals: Vec<Snip721Approval> = Vec::new();
3259 let mut owner_public: Option<Expiration> = None;
3260 let mut meta_public: Option<Expiration> = None;
3261 for perm in perm_list {
3262 if perm.address == global_raw {
3264 if let Some(exp) = perm.expirations[perm_type_info.view_owner_idx] {
3265 if !exp.is_expired(block) {
3266 owner_public = Some(exp);
3267 }
3268 }
3269 if let Some(exp) = perm.expirations[perm_type_info.view_meta_idx] {
3270 if !exp.is_expired(block) {
3271 meta_public = Some(exp);
3272 }
3273 }
3274 } else {
3276 let mut has_some = false;
3277 for i in 0..perm_type_info.num_types {
3278 perm.expirations[i] =
3279 perm.expirations[i].filter(|e| include_expired || !e.is_expired(block));
3280 if !has_some && perm.expirations[i].is_some() {
3281 has_some = true;
3282 }
3283 }
3284 if has_some {
3285 approvals.push(Snip721Approval {
3286 address: api.human_address(&perm.address)?,
3287 view_owner_expiration: perm.expirations[perm_type_info.view_owner_idx].take(),
3288 view_private_metadata_expiration: perm.expirations
3289 [perm_type_info.view_meta_idx]
3290 .take(),
3291 transfer_expiration: perm.expirations[perm_type_info.transfer_idx].take(),
3292 });
3293 }
3294 }
3295 }
3296 Ok((approvals, owner_public, meta_public))
3297}
3298
3299fn check_view_supply<S: Storage, A: Api, Q: Querier>(
3309 deps: &Extern<S, A, Q>,
3310 viewer: Option<ViewerInfo>,
3311 from_permit: Option<CanonicalAddr>,
3312) -> StdResult<()> {
3313 let config: Config = load(&deps.storage, CONFIG_KEY)?;
3314 let mut is_auth = config.token_supply_is_public;
3315 if !is_auth {
3316 let querier = get_querier(deps, viewer, from_permit)?;
3317 if let Some(viewer_raw) = querier {
3318 let minters: Vec<CanonicalAddr> =
3319 may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
3320 is_auth = minters.contains(&viewer_raw);
3321 }
3322 if !is_auth {
3323 return Err(StdError::generic_err(
3324 "The token supply of this contract is private",
3325 ));
3326 }
3327 }
3328 Ok(())
3329}
3330
3331fn check_key<S: ReadonlyStorage>(
3339 storage: &S,
3340 address: &CanonicalAddr,
3341 viewing_key: String,
3342) -> StdResult<()> {
3343 let read_key = ReadonlyPrefixedStorage::new(PREFIX_VIEW_KEY, storage);
3345 let load_key: [u8; VIEWING_KEY_SIZE] =
3346 may_load(&read_key, address.as_slice())?.unwrap_or([0u8; VIEWING_KEY_SIZE]);
3347 let input_key = ViewingKey(viewing_key);
3348 if input_key.check_viewing_key(&load_key) {
3350 return Ok(());
3351 }
3352 Err(StdError::generic_err(
3353 "Wrong viewing key for this address or viewing key not set",
3354 ))
3355}
3356
3357#[allow(clippy::too_many_arguments)]
3373pub fn check_permission<S: Storage, A: Api, Q: Querier>(
3374 deps: &Extern<S, A, Q>,
3375 block: &BlockInfo,
3376 token: &Token,
3377 token_id: &str,
3378 opt_sender: Option<&CanonicalAddr>,
3379 perm_type: PermissionType,
3380 oper_for: &mut Vec<CanonicalAddr>,
3381 custom_err: &str,
3382 owner_is_public: bool,
3383) -> StdResult<()> {
3384 let exp_idx = perm_type.to_usize();
3385 let owner_slice = token.owner.as_slice();
3386 if let PermissionType::ViewOwner = perm_type {
3389 let priv_store = ReadonlyPrefixedStorage::new(PREFIX_OWNER_PRIV, &deps.storage);
3390 let pass: bool = may_load(&priv_store, owner_slice)?.unwrap_or(owner_is_public);
3391 if pass {
3392 return Ok(());
3393 }
3394 }
3395 check_perm_core(
3396 deps,
3397 block,
3398 token,
3399 token_id,
3400 opt_sender,
3401 owner_slice,
3402 exp_idx,
3403 oper_for,
3404 custom_err,
3405 )
3406}
3407
3408#[allow(clippy::too_many_arguments)]
3424fn check_perm_core<S: Storage, A: Api, Q: Querier>(
3425 deps: &Extern<S, A, Q>,
3426 block: &BlockInfo,
3427 token: &Token,
3428 token_id: &str,
3429 opt_sender: Option<&CanonicalAddr>,
3430 owner_slice: &[u8],
3431 exp_idx: usize,
3432 oper_for: &mut Vec<CanonicalAddr>,
3433 custom_err: &str,
3434) -> StdResult<()> {
3435 if !oper_for.contains(&token.owner) {
3437 let mut err_msg = custom_err;
3438 let mut expired_msg = String::new();
3439 let global_raw = CanonicalAddr(Binary::from(b"public"));
3440 let (sender, only_public) = if let Some(sdr) = opt_sender {
3441 (sdr, false)
3442 } else {
3443 (&global_raw, true)
3444 };
3445 if token.owner == *sender {
3447 return Ok(());
3448 }
3449 let mut one_expired = only_public;
3454 let mut found_one = only_public;
3456 for perm in &token.permissions {
3457 if perm.address == *sender || perm.address == global_raw {
3458 if let Some(exp) = perm.expirations[exp_idx] {
3459 if !exp.is_expired(block) {
3460 return Ok(());
3461 } else {
3463 if perm.address != global_raw {
3465 expired_msg
3466 .push_str(&format!("Access to token {} has expired", token_id));
3467 err_msg = &expired_msg;
3468 }
3469 if one_expired {
3472 return Err(StdError::generic_err(err_msg));
3473 } else {
3474 one_expired = true;
3475 }
3476 }
3477 }
3478 if found_one {
3480 break;
3481 } else {
3482 found_one = true;
3483 }
3484 }
3485 }
3486 let all_store = ReadonlyPrefixedStorage::new(PREFIX_ALL_PERMISSIONS, &deps.storage);
3491 let may_list: Option<Vec<Permission>> = json_may_load(&all_store, owner_slice)?;
3492 found_one = only_public;
3493 if let Some(list) = may_list {
3494 for perm in &list {
3495 if perm.address == *sender || perm.address == global_raw {
3496 if let Some(exp) = perm.expirations[exp_idx] {
3497 if !exp.is_expired(block) {
3498 oper_for.push(token.owner.clone());
3499 return Ok(());
3500 } else if perm.address != global_raw {
3503 expired_msg.push_str(&format!(
3504 "Access to all tokens of {} has expired",
3505 &deps.api.human_address(&token.owner)?
3506 ));
3507 err_msg = &expired_msg;
3508 }
3509 }
3510 if found_one {
3512 return Err(StdError::generic_err(err_msg));
3513 } else {
3514 found_one = true;
3515 }
3516 }
3517 }
3518 }
3519 return Err(StdError::generic_err(err_msg));
3520 }
3521 Ok(())
3522}
3523
3524fn get_token_if_permitted<S: Storage, A: Api, Q: Querier>(
3538 deps: &Extern<S, A, Q>,
3539 block: &BlockInfo,
3540 token_id: &str,
3541 sender: Option<&CanonicalAddr>,
3542 perm_type: PermissionType,
3543 oper_for: &mut Vec<CanonicalAddr>,
3544 config: &Config,
3545) -> StdResult<(Token, u32)> {
3546 let custom_err = format!(
3547 "You are not authorized to perform this action on token {}",
3548 token_id
3549 );
3550 let opt_err = if config.token_supply_is_public {
3553 None
3554 } else {
3555 Some(&*custom_err)
3556 };
3557 let (token, idx) = get_token(&deps.storage, token_id, opt_err)?;
3558 check_permission(
3559 deps,
3560 block,
3561 &token,
3562 token_id,
3563 sender,
3564 perm_type,
3565 oper_for,
3566 &custom_err,
3567 config.owner_is_public,
3568 )?;
3569 Ok((token, idx))
3570}
3571
3572fn get_token<S: ReadonlyStorage>(
3583 storage: &S,
3584 token_id: &str,
3585 custom_err: Option<&str>,
3586) -> StdResult<(Token, u32)> {
3587 let default_err: String;
3588 let not_found = if let Some(err) = custom_err {
3589 err
3590 } else {
3591 default_err = format!("Token ID: {} not found", token_id);
3592 &*default_err
3593 };
3594 let map2idx = ReadonlyPrefixedStorage::new(PREFIX_MAP_TO_INDEX, storage);
3595 let idx: u32 =
3596 may_load(&map2idx, token_id.as_bytes())?.ok_or_else(|| StdError::generic_err(not_found))?;
3597 let info_store = ReadonlyPrefixedStorage::new(PREFIX_INFOS, storage);
3598 let token: Token = json_may_load(&info_store, &idx.to_le_bytes())?.ok_or_else(|| {
3599 StdError::generic_err(format!("Unable to find token info for {}", token_id))
3600 })?;
3601 Ok((token, idx))
3602}
3603
3604fn check_status(contract_status: u8, priority: u8) -> StdResult<()> {
3612 if priority < contract_status {
3613 return Err(StdError::generic_err(
3614 "The contract admin has temporarily disabled this action",
3615 ));
3616 }
3617 Ok(())
3618}
3619
3620#[allow(clippy::too_many_arguments)]
3632fn set_metadata_impl<S: Storage>(
3633 storage: &mut S,
3634 token: &Token,
3635 idx: u32,
3636 prefix: &[u8],
3637 metadata: &Metadata,
3638) -> StdResult<()> {
3639 if !token.unwrapped && prefix == PREFIX_PRIV_META {
3641 return Err(StdError::generic_err(
3642 "The private metadata of a sealed token can not be modified",
3643 ));
3644 }
3645 enforce_metadata_field_exclusion(metadata)?;
3646 let mut meta_store = PrefixedStorage::new(prefix, storage);
3647 save(&mut meta_store, &idx.to_le_bytes(), metadata)?;
3648 Ok(())
3649}
3650
3651pub enum SetAppResp {
3653 SetWhitelistedApproval,
3654 ApproveAll,
3655 RevokeAll,
3656}
3657
3658#[derive(Default)]
3660pub struct AlterAuthTable {
3661 pub add: [bool; 3],
3663 pub full: [bool; 3],
3665 pub remove: [bool; 3],
3667 pub clear: [bool; 3],
3669 pub has_update: bool,
3671}
3672
3673#[derive(Default)]
3675pub struct AlterPermTable {
3676 pub add: [bool; 3],
3678 pub remove: [bool; 3],
3680 pub has_update: bool,
3682}
3683
3684pub struct ProcessAccInfo {
3686 pub token: Token,
3688 pub idx: u32,
3690 pub token_given: bool,
3692 pub accesses: [Option<AccessLevel>; 3],
3694 pub expires: Option<Expiration>,
3696 pub from_oper: bool,
3698}
3699
3700fn process_accesses<S: Storage>(
3713 storage: &mut S,
3714 env: &Env,
3715 address: &CanonicalAddr,
3716 owner: &CanonicalAddr,
3717 proc_info: &mut ProcessAccInfo,
3718 all_perm_in: Option<Vec<Permission>>,
3719) -> StdResult<()> {
3720 let owner_slice = owner.as_slice();
3721 let expiration = proc_info.expires.unwrap_or_default();
3722 let expirations = vec![expiration; 3];
3723 let mut alt_all_perm = AlterPermTable::default();
3724 let mut alt_tok_perm = AlterPermTable::default();
3725 let mut alt_load_tok_perm = AlterPermTable::default();
3726 let mut alt_auth_list = AlterAuthTable::default();
3727 let mut add_load_list = Vec::new();
3728 let mut load_all = false;
3729 let mut load_all_exp = vec![Expiration::AtHeight(0); 3];
3730 let mut all_perm = if proc_info.from_oper {
3731 all_perm_in.ok_or_else(|| StdError::generic_err("Unable to get operator list"))?
3732 } else {
3733 Vec::new()
3734 };
3735 let mut oper_pos = 0usize;
3736 let mut found_perm = false;
3737 let mut tried_oper = false;
3738 let num_perm_types = PermissionType::ViewOwner.num_types();
3739
3740 for i in 0..num_perm_types {
3742 if let Some(acc) = &proc_info.accesses[i] {
3743 match acc {
3744 AccessLevel::ApproveToken | AccessLevel::RevokeToken => {
3745 if !proc_info.token_given {
3746 return Err(StdError::generic_err(
3747 "Attempted to grant/revoke permission for a token, but did not specify a token ID",
3748 ));
3749 }
3750 let is_approve = matches!(acc, AccessLevel::ApproveToken);
3751 if !tried_oper {
3753 if !proc_info.from_oper {
3754 let all_store =
3755 ReadonlyPrefixedStorage::new(PREFIX_ALL_PERMISSIONS, storage);
3756 all_perm = json_may_load(&all_store, owner_slice)?.unwrap_or_default();
3757 }
3758 if let Some(pos) = all_perm.iter().position(|p| p.address == *address) {
3759 found_perm = true;
3760 oper_pos = pos;
3761 }
3762 tried_oper = true;
3763 }
3764 if found_perm {
3766 if let Some(op) = all_perm.get(oper_pos) {
3767 if let Some(exp) = op.expirations[i] {
3768 if !exp.is_expired(&env.block) {
3769 if proc_info.from_oper {
3772 if is_approve {
3774 return Ok(());
3775 } else {
3777 return Err(StdError::generic_err(
3778 "Can not revoke transfer permission from an existing operator",
3779 ));
3780 }
3781 }
3782 if is_approve && expirations[i] == exp {
3786 return Ok(());
3787 }
3788 alt_auth_list.full[i] = true;
3790 load_all = true;
3792 load_all_exp[i] = exp;
3794 alt_load_tok_perm.add[i] = true;
3796 alt_load_tok_perm.has_update = true;
3797 }
3798 alt_all_perm.remove[i] = true;
3800 alt_all_perm.has_update = true;
3801 }
3802 }
3803 }
3804 if is_approve {
3805 alt_tok_perm.add[i] = true;
3807 alt_auth_list.add[i] = true;
3809 } else {
3810 alt_tok_perm.remove[i] = true;
3812 alt_auth_list.remove[i] = true;
3814 }
3815 alt_tok_perm.has_update = true;
3816 alt_auth_list.has_update = true;
3817 }
3818 AccessLevel::All | AccessLevel::None => {
3819 if let AccessLevel::All = acc {
3820 alt_all_perm.add[i] = true;
3822 } else {
3823 alt_all_perm.remove[i] = true;
3825 }
3826 alt_all_perm.has_update = true;
3827 alt_auth_list.clear[i] = true;
3829 alt_auth_list.has_update = true;
3830 if proc_info.token_given {
3832 alt_tok_perm.remove[i] = true;
3834 alt_tok_perm.has_update = true;
3835 }
3836 alt_load_tok_perm.remove[i] = true;
3838 alt_load_tok_perm.has_update = true;
3839 if !load_all {
3841 add_load_list.push(i);
3843 }
3844 }
3845 }
3846 }
3847 }
3848 if alt_all_perm.has_update {
3850 if !tried_oper {
3852 let all_store = ReadonlyPrefixedStorage::new(PREFIX_ALL_PERMISSIONS, storage);
3853 all_perm = json_may_load(&all_store, owner_slice)?.unwrap_or_default();
3854 }
3855 if alter_perm_list(
3857 &mut all_perm,
3858 &alt_all_perm,
3859 address,
3860 &expirations,
3861 num_perm_types,
3862 ) {
3863 let mut all_store = PrefixedStorage::new(PREFIX_ALL_PERMISSIONS, storage);
3864 if all_perm.is_empty() {
3866 remove(&mut all_store, owner_slice);
3867 } else {
3868 json_save(&mut all_store, owner_slice, &all_perm)?;
3869 }
3870 }
3871 }
3872 if alt_tok_perm.has_update && proc_info.token_given {
3876 if alter_perm_list(
3878 &mut proc_info.token.permissions,
3879 &alt_tok_perm,
3880 address,
3881 &expirations,
3882 num_perm_types,
3883 ) {
3884 let mut info_store = PrefixedStorage::new(PREFIX_INFOS, storage);
3885 json_save(
3886 &mut info_store,
3887 &proc_info.idx.to_le_bytes(),
3888 &proc_info.token,
3889 )?;
3890 }
3891 }
3892 if alt_auth_list.has_update {
3894 let auth_store = ReadonlyPrefixedStorage::new(PREFIX_AUTHLIST, storage);
3896 let mut auth_list: Vec<AuthList> = may_load(&auth_store, owner_slice)?.unwrap_or_default();
3897 let mut new_auth = AuthList {
3898 address: address.clone(),
3899 tokens: [Vec::new(), Vec::new(), Vec::new()],
3900 };
3901 let (auth, found, pos) =
3902 if let Some(pos) = auth_list.iter().position(|a| a.address == *address) {
3903 if let Some(a) = auth_list.get_mut(pos) {
3904 (a, true, pos)
3905 } else {
3907 (&mut new_auth, false, 0usize)
3908 }
3909 } else {
3911 (&mut new_auth, false, 0usize)
3912 };
3913 let load_list: HashSet<u32> = if alt_load_tok_perm.has_update {
3914 let list = if load_all {
3917 let inv = Inventory::new(storage, owner.clone())?;
3918 let mut set = inv.to_set(storage)?;
3919 set.remove(&proc_info.idx);
3921 set
3922 } else {
3924 let mut set: HashSet<u32> = HashSet::new();
3925 for l in add_load_list {
3926 set.extend(auth.tokens[l].iter());
3927 }
3928 if proc_info.token_given {
3930 set.remove(&proc_info.idx);
3931 }
3932 set
3933 };
3934 let mut info_store = PrefixedStorage::new(PREFIX_INFOS, storage);
3935 for t_i in &list {
3936 let tok_key = t_i.to_le_bytes();
3937 let may_tok: Option<Token> = json_may_load(&info_store, &tok_key)?;
3938 if let Some(mut load_tok) = may_tok {
3939 if load_tok.owner == *owner
3941 && alter_perm_list(
3942 &mut load_tok.permissions,
3943 &alt_load_tok_perm,
3944 address,
3945 &load_all_exp,
3946 num_perm_types,
3947 )
3948 {
3949 json_save(&mut info_store, &tok_key, &load_tok)?;
3950 }
3951 }
3952 }
3953 list
3954 } else {
3955 HashSet::new()
3956 };
3957 let mut updated = false;
3958 for i in 0..num_perm_types {
3960 if alt_auth_list.clear[i] {
3962 if !auth.tokens[i].is_empty() {
3963 auth.tokens[i].clear();
3964 updated = true;
3965 }
3966 } else if alt_auth_list.full[i] {
3968 auth.tokens[i] = load_list.iter().copied().collect();
3969 if alt_auth_list.add[i] {
3972 auth.tokens[i].push(proc_info.idx);
3973 }
3974 updated = true;
3975 } else if alt_auth_list.add[i] && proc_info.token_given {
3977 if !auth.tokens[i].contains(&proc_info.idx) {
3978 auth.tokens[i].push(proc_info.idx);
3979 updated = true;
3980 }
3981 } else if alt_auth_list.remove[i] && proc_info.token_given {
3983 if let Some(tok_pos) = auth.tokens[i].iter().position(|&t| t == proc_info.idx) {
3984 auth.tokens[i].swap_remove(tok_pos);
3985 updated = true;
3986 }
3987 }
3988 }
3989 if updated {
3991 let mut auth_store = PrefixedStorage::new(PREFIX_AUTHLIST, storage);
3992 let mut save_it = true;
3993 if auth.tokens.iter().all(|t| t.is_empty()) {
3995 if found {
3997 if auth_list.len() == 1 {
4000 remove(&mut auth_store, owner_slice);
4001 save_it = false;
4002 } else {
4003 auth_list.swap_remove(pos);
4004 }
4005 } else {
4007 save_it = false;
4008 }
4009 } else {
4011 if !found {
4013 auth_list.push(new_auth);
4014 }
4015 }
4016 if save_it {
4017 save(&mut auth_store, owner_slice, &auth_list)?;
4018 }
4019 }
4020 }
4021 Ok(())
4022}
4023
4024fn alter_perm_list(
4036 perms: &mut Vec<Permission>,
4037 alter_table: &AlterPermTable,
4038 address: &CanonicalAddr,
4039 expiration: &[Expiration],
4040 num_perm_types: usize,
4041) -> bool {
4042 let mut updated = false;
4043 let mut new_perm = Permission {
4044 address: address.clone(),
4045 expirations: [None; 3],
4046 };
4047 let (perm, found, pos) = if let Some(pos) = perms.iter().position(|p| p.address == *address) {
4048 if let Some(p) = perms.get_mut(pos) {
4049 (p, true, pos)
4050 } else {
4052 (&mut new_perm, false, 0usize)
4053 }
4054 } else {
4056 (&mut new_perm, false, 0usize)
4057 };
4058 #[allow(clippy::needless_range_loop)]
4060 for i in 0..num_perm_types {
4061 if alter_table.add[i] {
4063 if let Some(old_exp) = perm.expirations[i] {
4065 if old_exp != expiration[i] {
4067 perm.expirations[i] = Some(expiration[i]);
4068 updated = true;
4069 }
4070 } else {
4072 perm.expirations[i] = Some(expiration[i]);
4073 updated = true;
4074 }
4075 } else if alter_table.remove[i] {
4077 if perm.expirations[i].is_some() {
4079 perm.expirations[i] = None;
4081 updated = true;
4082 }
4083 }
4084 }
4085 if updated {
4087 if !found {
4089 perms.push(new_perm);
4090 } else if perm.expirations.iter().all(|&e| e.is_none()) {
4092 perms.swap_remove(pos);
4093 }
4094 }
4095 updated
4096}
4097
4098pub struct CacheReceiverInfo {
4100 pub contract: CanonicalAddr,
4102 pub registration: ReceiveRegistration,
4104}
4105
4106#[allow(clippy::too_many_arguments)]
4121fn receiver_callback_msgs<S: ReadonlyStorage>(
4122 storage: &S,
4123 contract_human: &HumanAddr,
4124 contract: &CanonicalAddr,
4125 receiver_info: Option<ReceiverInfo>,
4126 send_from_list: Vec<SendFrom>,
4127 msg: &Option<Binary>,
4128 sender: &HumanAddr,
4129 receivers: &mut Vec<CacheReceiverInfo>,
4130) -> StdResult<Vec<CosmosMsg>> {
4131 let (code_hash, impl_batch) = if let Some(supplied) = receiver_info {
4132 (
4133 supplied.recipient_code_hash,
4134 supplied.also_implements_batch_receive_nft.unwrap_or(false),
4135 )
4136 } else if let Some(receiver) = receivers.iter().find(|&r| r.contract == *contract) {
4137 (
4138 receiver.registration.code_hash.clone(),
4139 receiver.registration.impl_batch,
4140 )
4141 } else {
4142 let store = ReadonlyPrefixedStorage::new(PREFIX_RECEIVERS, storage);
4143 let registration: ReceiveRegistration =
4144 may_load(&store, contract.as_slice())?.unwrap_or(ReceiveRegistration {
4145 code_hash: String::new(),
4146 impl_batch: false,
4147 });
4148 let receiver = CacheReceiverInfo {
4149 contract: contract.clone(),
4150 registration: registration.clone(),
4151 };
4152 receivers.push(receiver);
4153 (registration.code_hash, registration.impl_batch)
4154 };
4155 if code_hash.is_empty() {
4156 return Ok(Vec::new());
4157 }
4158 let mut callbacks: Vec<CosmosMsg> = Vec::new();
4159 for send_from in send_from_list.into_iter() {
4160 if impl_batch {
4162 callbacks.push(batch_receive_nft_msg(
4163 sender.clone(),
4164 send_from.owner,
4165 send_from.token_ids,
4166 msg.clone(),
4167 code_hash.clone(),
4168 contract_human.clone(),
4169 )?);
4170 } else {
4172 for token_id in send_from.token_ids.into_iter() {
4173 callbacks.push(receive_nft_msg(
4174 send_from.owner.clone(),
4175 token_id,
4176 msg.clone(),
4177 code_hash.clone(),
4178 contract_human.clone(),
4179 )?);
4180 }
4181 }
4182 }
4183 Ok(callbacks)
4184}
4185
4186pub struct InventoryUpdate {
4188 pub inventory: Inventory,
4190 pub remove: HashSet<u32>,
4192}
4193
4194fn update_owner_inventory<S: Storage>(
4204 storage: &mut S,
4205 updates: &[InventoryUpdate],
4206 num_perm_types: usize,
4207) -> StdResult<()> {
4208 for update in updates {
4209 let owner_slice = update.inventory.owner.as_slice();
4210 update.inventory.save(storage)?;
4212 if !update.remove.is_empty() {
4214 let mut auth_store = PrefixedStorage::new(PREFIX_AUTHLIST, storage);
4215 let may_list: Option<Vec<AuthList>> = may_load(&auth_store, owner_slice)?;
4216 if let Some(list) = may_list {
4217 let mut new_list = Vec::new();
4218 for mut auth in list.into_iter() {
4219 for i in 0..num_perm_types {
4220 auth.tokens[i].retain(|t| !update.remove.contains(t));
4221 }
4222 if !auth.tokens.iter().all(|u| u.is_empty()) {
4223 new_list.push(auth)
4224 }
4225 }
4226 if new_list.is_empty() {
4227 remove(&mut auth_store, owner_slice);
4228 } else {
4229 save(&mut auth_store, owner_slice, &new_list)?;
4230 }
4231 }
4232 }
4233 }
4234 Ok(())
4235}
4236
4237#[allow(clippy::too_many_arguments)]
4253fn transfer_impl<S: Storage, A: Api, Q: Querier>(
4254 deps: &mut Extern<S, A, Q>,
4255 block: &BlockInfo,
4256 config: &mut Config,
4257 sender: &CanonicalAddr,
4258 token_id: String,
4259 recipient: CanonicalAddr,
4260 oper_for: &mut Vec<CanonicalAddr>,
4261 inv_updates: &mut Vec<InventoryUpdate>,
4262 memo: Option<String>,
4263) -> StdResult<CanonicalAddr> {
4264 let (mut token, idx) = get_token_if_permitted(
4265 deps,
4266 block,
4267 &token_id,
4268 Some(sender),
4269 PermissionType::Transfer,
4270 oper_for,
4271 config,
4272 )?;
4273 if !token.transferable {
4274 return Err(StdError::generic_err(format!(
4275 "Token ID: {} is non-transferable",
4276 token_id
4277 )));
4278 }
4279 let old_owner = token.owner;
4280 if old_owner == recipient {
4282 return Err(StdError::generic_err(format!(
4283 "Attempting to transfer token ID: {} to the address that already owns it",
4284 &token_id
4285 )));
4286 }
4287 token.owner = recipient.clone();
4288 token.permissions.clear();
4289
4290 let update_addrs = vec![recipient.clone(), old_owner.clone()];
4291 let mut info_store = PrefixedStorage::new(PREFIX_INFOS, &mut deps.storage);
4293 json_save(&mut info_store, &idx.to_le_bytes(), &token)?;
4294 for addr in update_addrs.into_iter() {
4296 let inv_upd = if let Some(inv) = inv_updates.iter_mut().find(|i| i.inventory.owner == addr)
4297 {
4298 inv
4299 } else {
4300 let inventory = Inventory::new(&deps.storage, addr)?;
4301 let new_inv = InventoryUpdate {
4302 inventory,
4303 remove: HashSet::new(),
4304 };
4305 inv_updates.push(new_inv);
4306 inv_updates.last_mut().ok_or_else(|| {
4307 StdError::generic_err("Just pushed an InventoryUpdate so this can not happen")
4308 })?
4309 };
4310 if inv_upd.inventory.owner == recipient {
4312 inv_upd.inventory.insert(&mut deps.storage, idx, false)?;
4313 } else {
4315 inv_upd.inventory.remove(&mut deps.storage, idx, false)?;
4316 inv_upd.remove.insert(idx);
4317 }
4318 }
4319
4320 let sndr = if old_owner == *sender {
4321 None
4322 } else {
4323 Some(sender.clone())
4324 };
4325 store_transfer(
4327 &mut deps.storage,
4328 config,
4329 block,
4330 token_id,
4331 old_owner.clone(),
4332 sndr,
4333 recipient,
4334 memo,
4335 )?;
4336 Ok(old_owner)
4337}
4338
4339pub struct SendFrom {
4341 pub owner: HumanAddr,
4343 pub token_ids: Vec<String>,
4345}
4346
4347fn send_list<S: Storage, A: Api, Q: Querier>(
4360 deps: &mut Extern<S, A, Q>,
4361 env: &Env,
4362 config: &mut Config,
4363 sender: &CanonicalAddr,
4364 transfers: Option<Vec<Transfer>>,
4365 sends: Option<Vec<Send>>,
4366) -> StdResult<Vec<CosmosMsg>> {
4367 let mut messages: Vec<CosmosMsg> = Vec::new();
4368 let mut oper_for: Vec<CanonicalAddr> = Vec::new();
4369 let mut inv_updates: Vec<InventoryUpdate> = Vec::new();
4370 let num_perm_types = PermissionType::ViewOwner.num_types();
4371 if let Some(xfers) = transfers {
4372 for xfer in xfers.into_iter() {
4373 let recipient_raw = deps.api.canonical_address(&xfer.recipient)?;
4374 for token_id in xfer.token_ids.into_iter() {
4375 let _o = transfer_impl(
4376 deps,
4377 &env.block,
4378 config,
4379 sender,
4380 token_id,
4381 recipient_raw.clone(),
4382 &mut oper_for,
4383 &mut inv_updates,
4384 xfer.memo.clone(),
4385 )?;
4386 }
4387 }
4388 } else if let Some(snds) = sends {
4389 let mut receivers = Vec::new();
4390 for send in snds.into_iter() {
4391 let contract_raw = deps.api.canonical_address(&send.contract)?;
4392 let mut send_from_list: Vec<SendFrom> = Vec::new();
4393 for token_id in send.token_ids.into_iter() {
4394 let owner_raw = transfer_impl(
4395 deps,
4396 &env.block,
4397 config,
4398 sender,
4399 token_id.clone(),
4400 contract_raw.clone(),
4401 &mut oper_for,
4402 &mut inv_updates,
4403 send.memo.clone(),
4404 )?;
4405 let owner = deps.api.human_address(&owner_raw)?;
4407 if let Some(sd_fm) = send_from_list.iter_mut().find(|s| s.owner == owner) {
4408 sd_fm.token_ids.push(token_id.clone());
4409 } else {
4410 let new_sd_fm = SendFrom {
4411 owner,
4412 token_ids: vec![token_id.clone()],
4413 };
4414 send_from_list.push(new_sd_fm);
4415 }
4416 }
4417 messages.extend(receiver_callback_msgs(
4419 &deps.storage,
4420 &send.contract,
4421 &contract_raw,
4422 send.receiver_info,
4423 send_from_list,
4424 &send.msg,
4425 &env.message.sender,
4426 &mut receivers,
4427 )?);
4428 }
4429 }
4430 save(&mut deps.storage, CONFIG_KEY, &config)?;
4431 update_owner_inventory(&mut deps.storage, &inv_updates, num_perm_types)?;
4432 Ok(messages)
4433}
4434
4435fn burn_list<S: Storage, A: Api, Q: Querier>(
4447 deps: &mut Extern<S, A, Q>,
4448 block: &BlockInfo,
4449 config: &mut Config,
4450 sender: &CanonicalAddr,
4451 burns: Vec<Burn>,
4452) -> StdResult<()> {
4453 let mut oper_for: Vec<CanonicalAddr> = Vec::new();
4454 let mut inv_updates: Vec<InventoryUpdate> = Vec::new();
4455 let num_perm_types = PermissionType::ViewOwner.num_types();
4456 for burn in burns.into_iter() {
4457 for token_id in burn.token_ids.into_iter() {
4458 let (token, idx) = get_token_if_permitted(
4459 deps,
4460 block,
4461 &token_id,
4462 Some(sender),
4463 PermissionType::Transfer,
4464 &mut oper_for,
4465 config,
4466 )?;
4467 if !config.burn_is_enabled && token.transferable {
4468 return Err(StdError::generic_err(
4469 "Burn functionality is not enabled for this token",
4470 ));
4471 }
4472 let inv_upd = if let Some(inv) = inv_updates
4474 .iter_mut()
4475 .find(|i| i.inventory.owner == token.owner)
4476 {
4477 inv
4478 } else {
4479 let inventory = Inventory::new(&deps.storage, token.owner.clone())?;
4480 let new_inv = InventoryUpdate {
4481 inventory,
4482 remove: HashSet::new(),
4483 };
4484 inv_updates.push(new_inv);
4485 inv_updates.last_mut().ok_or_else(|| {
4486 StdError::generic_err("Just pushed an InventoryUpdate so this can not happen")
4487 })?
4488 };
4489 inv_upd.inventory.remove(&mut deps.storage, idx, false)?;
4490 inv_upd.remove.insert(idx);
4491 let token_key = idx.to_le_bytes();
4492 config.token_cnt = config.token_cnt.saturating_sub(1);
4494 let mut map2idx = PrefixedStorage::new(PREFIX_MAP_TO_INDEX, &mut deps.storage);
4496 remove(&mut map2idx, token_id.as_bytes());
4497 let mut map2id = PrefixedStorage::new(PREFIX_MAP_TO_ID, &mut deps.storage);
4498 remove(&mut map2id, &token_key);
4499 let mut info_store = PrefixedStorage::new(PREFIX_INFOS, &mut deps.storage);
4501 remove(&mut info_store, &token_key);
4502 let mut pub_store = PrefixedStorage::new(PREFIX_PUB_META, &mut deps.storage);
4504 remove(&mut pub_store, &token_key);
4505 let mut priv_store = PrefixedStorage::new(PREFIX_PRIV_META, &mut deps.storage);
4506 remove(&mut priv_store, &token_key);
4507 let mut run_store = PrefixedStorage::new(PREFIX_MINT_RUN, &mut deps.storage);
4509 remove(&mut run_store, &token_key);
4510 let mut roy_store = PrefixedStorage::new(PREFIX_ROYALTY_INFO, &mut deps.storage);
4512 remove(&mut roy_store, &token_key);
4513
4514 let brnr = if token.owner == *sender {
4515 None
4516 } else {
4517 Some(sender.clone())
4518 };
4519 store_burn(
4521 &mut deps.storage,
4522 config,
4523 block,
4524 token_id,
4525 token.owner,
4526 brnr,
4527 burn.memo.clone(),
4528 )?;
4529 }
4530 }
4531 save(&mut deps.storage, CONFIG_KEY, &config)?;
4532 update_owner_inventory(&mut deps.storage, &inv_updates, num_perm_types)?;
4533 Ok(())
4534}
4535
4536fn mint_list<S: Storage, A: Api, Q: Querier>(
4548 deps: &mut Extern<S, A, Q>,
4549 env: &Env,
4550 config: &mut Config,
4551 sender_raw: &CanonicalAddr,
4552 mints: Vec<Mint>,
4553) -> StdResult<Vec<String>> {
4554 let mut inventories: Vec<Inventory> = Vec::new();
4555 let mut minted: Vec<String> = Vec::new();
4556 let default_roy: Option<StoredRoyaltyInfo> = may_load(&deps.storage, DEFAULT_ROYALTY_KEY)?;
4557 for mint in mints.into_iter() {
4558 let id = mint.token_id.unwrap_or(format!("{}", config.mint_cnt));
4559 let mut map2idx = PrefixedStorage::new(PREFIX_MAP_TO_INDEX, &mut deps.storage);
4561 let may_exist: Option<u32> = may_load(&map2idx, id.as_bytes())?;
4562 if may_exist.is_some() {
4563 return Err(StdError::generic_err(format!(
4564 "Token ID {} is already in use",
4565 id
4566 )));
4567 }
4568 config.token_cnt = config.token_cnt.checked_add(1).ok_or_else(|| {
4570 StdError::generic_err("Attempting to mint more tokens than the implementation limit")
4571 })?;
4572 save(&mut map2idx, id.as_bytes(), &config.mint_cnt)?;
4574 let recipient = if let Some(o) = mint.owner {
4575 deps.api.canonical_address(&o)?
4576 } else {
4577 sender_raw.clone()
4578 };
4579 let transferable = mint.transferable.unwrap_or(true);
4580 let token = Token {
4581 owner: recipient.clone(),
4582 permissions: Vec::new(),
4583 unwrapped: !config.sealed_metadata_is_enabled,
4584 transferable,
4585 };
4586
4587 let token_key = config.mint_cnt.to_le_bytes();
4589 let mut info_store = PrefixedStorage::new(PREFIX_INFOS, &mut deps.storage);
4590 json_save(&mut info_store, &token_key, &token)?;
4591 let inventory = if let Some(inv) = inventories.iter_mut().find(|i| i.owner == token.owner) {
4593 inv
4594 } else {
4595 let new_inv = Inventory::new(&deps.storage, token.owner.clone())?;
4596 inventories.push(new_inv);
4597 inventories.last_mut().ok_or_else(|| {
4598 StdError::generic_err("Just pushed an Inventory so this can not happen")
4599 })?
4600 };
4601 inventory.insert(&mut deps.storage, config.mint_cnt, false)?;
4602
4603 let mut map2id = PrefixedStorage::new(PREFIX_MAP_TO_ID, &mut deps.storage);
4605 save(&mut map2id, &token_key, &id)?;
4606
4607 if let Some(pub_meta) = mint.public_metadata {
4613 enforce_metadata_field_exclusion(&pub_meta)?;
4614 let mut pub_store = PrefixedStorage::new(PREFIX_PUB_META, &mut deps.storage);
4615 save(&mut pub_store, &token_key, &pub_meta)?;
4616 }
4617 if let Some(priv_meta) = mint.private_metadata {
4618 enforce_metadata_field_exclusion(&priv_meta)?;
4619 let mut priv_store = PrefixedStorage::new(PREFIX_PRIV_META, &mut deps.storage);
4620 save(&mut priv_store, &token_key, &priv_meta)?;
4621 }
4622 let (mint_run, serial_number, quantity_minted_this_run) =
4624 if let Some(ser) = mint.serial_number {
4625 (
4626 ser.mint_run,
4627 Some(ser.serial_number),
4628 ser.quantity_minted_this_run,
4629 )
4630 } else {
4631 (None, None, None)
4632 };
4633 let mint_info = StoredMintRunInfo {
4634 token_creator: sender_raw.clone(),
4635 time_of_minting: env.block.time,
4636 mint_run,
4637 serial_number,
4638 quantity_minted_this_run,
4639 };
4640 let mut run_store = PrefixedStorage::new(PREFIX_MINT_RUN, &mut deps.storage);
4641 save(&mut run_store, &token_key, &mint_info)?;
4642 if token.transferable {
4644 let mut roy_store = PrefixedStorage::new(PREFIX_ROYALTY_INFO, &mut deps.storage);
4645 store_royalties(
4646 &mut roy_store,
4647 &deps.api,
4648 mint.royalty_info.as_ref(),
4649 default_roy.as_ref(),
4650 &token_key,
4651 )?;
4652 }
4653 store_mint(
4658 &mut deps.storage,
4659 config,
4660 &env.block,
4661 id.clone(),
4662 sender_raw.clone(),
4663 recipient,
4664 mint.memo,
4665 )?;
4666 minted.push(id);
4667 config.mint_cnt = config.mint_cnt.checked_add(1).ok_or_else(|| {
4669 StdError::generic_err("Attempting to mint more times than the implementation limit")
4670 })?;
4671 }
4672 for inventory in inventories.iter() {
4674 inventory.save(&mut deps.storage)?;
4675 }
4676 save(&mut deps.storage, CONFIG_KEY, &config)?;
4677 Ok(minted)
4678}
4679
4680fn store_royalties<S: Storage, A: Api>(
4694 storage: &mut S,
4695 api: &A,
4696 royalty_info: Option<&RoyaltyInfo>,
4697 default: Option<&StoredRoyaltyInfo>,
4698 key: &[u8],
4699) -> StdResult<()> {
4700 if let Some(royal_inf) = royalty_info {
4702 let total_rates: u128 = royal_inf.royalties.iter().map(|r| r.rate as u128).sum();
4704 let (royalty_den, overflow) =
4705 U256::from(10).overflowing_pow(U256::from(royal_inf.decimal_places_in_rates));
4706 if overflow {
4707 return Err(StdError::generic_err(
4708 "The number of decimal places used in the royalty rates is larger than supported",
4709 ));
4710 }
4711 if U256::from(total_rates) > royalty_den {
4712 return Err(StdError::generic_err(
4713 "The sum of royalty rates must not exceed 100%",
4714 ));
4715 }
4716 let stored = royal_inf.to_stored(api)?;
4717 save(storage, key, &stored)
4718 } else if let Some(def) = default {
4719 save(storage, key, def)
4720 } else {
4721 remove(storage, key);
4722 Ok(())
4723 }
4724}
4725
4726fn enforce_metadata_field_exclusion(metadata: &Metadata) -> StdResult<()> {
4734 if metadata.token_uri.is_some() && metadata.extension.is_some() {
4735 return Err(StdError::generic_err(
4736 "Metadata can not have BOTH token_uri AND extension",
4737 ));
4738 }
4739 Ok(())
4740}
4741
4742fn get_querier<S: Storage, A: Api, Q: Querier>(
4751 deps: &Extern<S, A, Q>,
4752 viewer: Option<ViewerInfo>,
4753 from_permit: Option<CanonicalAddr>,
4754) -> StdResult<Option<CanonicalAddr>> {
4755 if from_permit.is_some() {
4756 return Ok(from_permit);
4757 }
4758 let viewer_raw = viewer
4759 .map(|v| {
4760 let raw = deps.api.canonical_address(&v.address)?;
4761 check_key(&deps.storage, &raw, v.viewing_key)?;
4762 Ok(raw)
4763 })
4764 .transpose()?;
4765 Ok(viewer_raw)
4766}
4767
4768pub struct OwnerInfo {
4770 pub owner: CanonicalAddr,
4772 pub owner_is_public: bool,
4774 pub inventory_approvals: Vec<Snip721Approval>,
4776 pub view_owner_exp: Option<Expiration>,
4778 pub view_meta_exp: Option<Expiration>,
4780}
4781
4782pub fn dossier_list<S: Storage, A: Api, Q: Querier>(
4795 deps: &Extern<S, A, Q>,
4796 token_ids: Vec<String>,
4797 viewer: Option<ViewerInfo>,
4798 include_expired: Option<bool>,
4799 from_permit: Option<CanonicalAddr>,
4800) -> StdResult<Vec<BatchNftDossierElement>> {
4801 let viewer_raw = get_querier(deps, viewer, from_permit)?;
4802 let opt_viewer = viewer_raw.as_ref();
4803 let incl_exp = include_expired.unwrap_or(false);
4804 let config: Config = load(&deps.storage, CONFIG_KEY)?;
4805 let contract_creator = deps
4806 .api
4807 .human_address(&load::<CanonicalAddr, _>(&deps.storage, CREATOR_KEY)?)?;
4808
4809 let block: BlockInfo = may_load(&deps.storage, BLOCK_KEY)?.unwrap_or_else(|| BlockInfo {
4811 height: 1,
4812 time: 1,
4813 chain_id: "not used".to_string(),
4814 });
4815 let perm_type_info = PermissionTypeInfo {
4816 view_owner_idx: PermissionType::ViewOwner.to_usize(),
4817 view_meta_idx: PermissionType::ViewMetadata.to_usize(),
4818 transfer_idx: PermissionType::Transfer.to_usize(),
4819 num_types: PermissionType::Transfer.num_types(),
4820 };
4821 let mut owner_oper_for: Vec<CanonicalAddr> = Vec::new();
4823 let mut meta_oper_for: Vec<CanonicalAddr> = Vec::new();
4824 let mut xfer_oper_for: Vec<CanonicalAddr> = Vec::new();
4825 let mut owner_cache: Vec<OwnerInfo> = Vec::new();
4826 let mut dossiers: Vec<BatchNftDossierElement> = Vec::new();
4827 let own_priv_store = ReadonlyPrefixedStorage::new(PREFIX_OWNER_PRIV, &deps.storage);
4829 let pub_store = ReadonlyPrefixedStorage::new(PREFIX_PUB_META, &deps.storage);
4830 let priv_store = ReadonlyPrefixedStorage::new(PREFIX_PRIV_META, &deps.storage);
4831 let roy_store = ReadonlyPrefixedStorage::new(PREFIX_ROYALTY_INFO, &deps.storage);
4832 let run_store = ReadonlyPrefixedStorage::new(PREFIX_MINT_RUN, &deps.storage);
4833 let all_store = ReadonlyPrefixedStorage::new(PREFIX_ALL_PERMISSIONS, &deps.storage);
4834
4835 for id in token_ids.into_iter() {
4836 let err_msg = format!(
4837 "You are not authorized to perform this action on token {}",
4838 &id
4839 );
4840 let opt_err = if config.token_supply_is_public {
4843 None
4844 } else {
4845 Some(&*err_msg)
4846 };
4847 let (mut token, idx) = get_token(&deps.storage, &id, opt_err)?;
4848 let owner_slice = token.owner.as_slice();
4849 let owner_inf = if let Some(inf) = owner_cache.iter().find(|o| o.owner == token.owner) {
4851 inf
4852 } else {
4853 let owner_is_public: bool =
4854 may_load(&own_priv_store, owner_slice)?.unwrap_or(config.owner_is_public);
4855 let mut all_perm: Vec<Permission> =
4856 json_may_load(&all_store, owner_slice)?.unwrap_or_default();
4857 let (inventory_approvals, view_owner_exp, view_meta_exp) =
4858 gen_snip721_approvals(&deps.api, &block, &mut all_perm, incl_exp, &perm_type_info)?;
4859 owner_cache.push(OwnerInfo {
4860 owner: token.owner.clone(),
4861 owner_is_public,
4862 inventory_approvals,
4863 view_owner_exp,
4864 view_meta_exp,
4865 });
4866 owner_cache.last().ok_or_else(|| {
4867 StdError::generic_err("This can't happen since we just pushed an OwnerInfo!")
4868 })?
4869 };
4870 let global_pass = owner_inf.owner_is_public;
4871 let owner = if global_pass
4873 || check_perm_core(
4874 deps,
4875 &block,
4876 &token,
4877 &id,
4878 opt_viewer,
4879 owner_slice,
4880 perm_type_info.view_owner_idx,
4881 &mut owner_oper_for,
4882 &err_msg,
4883 )
4884 .is_ok()
4885 {
4886 Some(deps.api.human_address(&token.owner)?)
4887 } else {
4888 None
4889 };
4890 let token_key = idx.to_le_bytes();
4892 let public_metadata: Option<Metadata> = may_load(&pub_store, &token_key)?;
4893 let mut display_private_metadata_error = None;
4895 let private_metadata = if let Err(err) = check_perm_core(
4896 deps,
4897 &block,
4898 &token,
4899 &id,
4900 opt_viewer,
4901 owner_slice,
4902 perm_type_info.view_meta_idx,
4903 &mut meta_oper_for,
4904 &err_msg,
4905 ) {
4906 if let StdError::GenericErr { msg, .. } = err {
4907 display_private_metadata_error = Some(msg);
4908 }
4909 None
4910 } else if !token.unwrapped {
4911 display_private_metadata_error = Some(format!(
4912 "Sealed metadata of token {} must be unwrapped by calling Reveal before it can be viewed", &id
4913 ));
4914 None
4915 } else {
4916 let priv_meta: Option<Metadata> = may_load(&priv_store, &token_key)?;
4917 priv_meta
4918 };
4919 let may_roy_inf: Option<StoredRoyaltyInfo> = may_load(&roy_store, &token_key)?;
4921 let royalty_info = may_roy_inf
4922 .map(|r| {
4923 let hide_addr = check_perm_core(
4924 deps,
4925 &block,
4926 &token,
4927 &id,
4928 opt_viewer,
4929 owner_slice,
4930 perm_type_info.transfer_idx,
4931 &mut xfer_oper_for,
4932 &err_msg,
4933 )
4934 .is_err();
4935 r.to_human(&deps.api, hide_addr)
4936 })
4937 .transpose()?;
4938 let mint_run: StoredMintRunInfo = load(&run_store, &token_key)?;
4940 let (token_approv, token_owner_exp, token_meta_exp) = gen_snip721_approvals(
4942 &deps.api,
4943 &block,
4944 &mut token.permissions,
4945 incl_exp,
4946 &perm_type_info,
4947 )?;
4948 let (public_ownership_expiration, owner_is_public) = if global_pass {
4950 (Some(Expiration::Never), true)
4951 } else if token_owner_exp.is_some() {
4952 (token_owner_exp, true)
4953 } else {
4954 (
4955 owner_inf.view_owner_exp.as_ref().cloned(),
4956 owner_inf.view_owner_exp.is_some(),
4957 )
4958 };
4959 let (private_metadata_is_public_expiration, private_metadata_is_public) =
4961 if token_meta_exp.is_some() {
4962 (token_meta_exp, true)
4963 } else {
4964 (
4965 owner_inf.view_meta_exp.as_ref().cloned(),
4966 owner_inf.view_meta_exp.is_some(),
4967 )
4968 };
4969 let (token_approvals, inventory_approvals) = opt_viewer.map_or((None, None), |v| {
4971 if token.owner == *v {
4972 (
4973 Some(token_approv),
4974 Some(owner_inf.inventory_approvals.clone()),
4975 )
4976 } else {
4977 (None, None)
4978 }
4979 });
4980 dossiers.push(BatchNftDossierElement {
4981 token_id: id,
4982 owner,
4983 public_metadata,
4984 private_metadata,
4985 royalty_info,
4986 mint_run_info: Some(mint_run.to_human(&deps.api, contract_creator.clone())?),
4987 transferable: token.transferable,
4988 unwrapped: token.unwrapped,
4989 display_private_metadata_error,
4990 owner_is_public,
4991 public_ownership_expiration,
4992 private_metadata_is_public,
4993 private_metadata_is_public_expiration,
4994 token_approvals,
4995 inventory_approvals,
4996 });
4997 }
4998 Ok(dossiers)
4999}