hmip721/
contract.rs

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;
10/// This contract implements SNIP-721 standard:
11/// https://github.com/SecretFoundation/SNIPs/blob/master/SNIP-721.md
12use 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
36/// pad handle responses and log attributes to blocks of 256 bytes to prevent leaking info based on
37/// response size
38pub const BLOCK_SIZE: usize = 256;
39/// max number of token ids to keep in id list block
40pub const ID_BLOCK_SIZE: u32 = 64;
41
42////////////////////////////////////// Init ///////////////////////////////////////
43/// Returns InitResult
44///
45/// Initializes the contract
46///
47/// # Arguments
48///
49/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
50/// * `env` - Env of contract's environment
51/// * `msg` - InitMsg passed in with the instantiation message
52pub 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    // TODO remove this after BlockInfo becomes available to queries
94    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    // perform the post init callback if needed
107    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
124///////////////////////////////////// Handle //////////////////////////////////////
125/// Returns HandleResult
126///
127/// # Arguments
128///
129/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
130/// * `env` - Env of contract's environment
131/// * `msg` - HandleMsg passed in with the execute message
132pub fn handle<S: Storage, A: Api, Q: Querier>(
133    deps: &mut Extern<S, A, Q>,
134    env: Env,
135    msg: HandleMsg,
136) -> HandleResult {
137    // TODO remove this after BlockInfo becomes available to queries
138    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/// Returns HandleResult
449///
450/// mint a new token
451///
452/// # Arguments
453///
454/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
455/// * `env` - Env of contract's environment
456/// * `config` - a mutable reference to the Config
457/// * `priority` - u8 representation of highest status level this action is permitted at
458/// * `token_id` - optional token id, if not specified, use token index
459/// * `owner` - optional owner of this token, if not specified, use the minter's address
460/// * `public_metadata` - optional public metadata viewable by everyone
461/// * `private_metadata` - optional private metadata viewable only by owner and whitelist
462/// * `serial_number` - optional serial number information for this token
463/// * `royalty_info` - optional royalties information for this token
464/// * `transferable` - optionally true if this token is transferable
465/// * `memo` - optional memo for the mint tx
466#[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
510/// Returns HandleResult
511///
512/// mints many tokens
513///
514/// # Arguments
515///
516/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
517/// * `env` - Env of contract's environment
518/// * `config` - a mutable reference to the Config
519/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
520/// * `mints` - the list of mints to perform
521pub 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/// Returns HandleResult
547///
548/// mints clones of a token
549///
550/// # Arguments
551///
552/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
553/// * `env` - Env of contract's environment
554/// * `config` - a mutable reference to the Config
555/// * `priority` - u8 representation of highest status level this action is permitted at
556/// * `mint_run_id` - optional id used to track subsequent mint runs
557/// * `quantity` - number of clones to mint
558/// * `owner` - optional owner of this token, if not specified, use the minter's address
559/// * `public_metadata` - optional public metadata viewable by everyone
560/// * `private_metadata` - optional private metadata viewable only by owner and whitelist
561/// * `royalty_info` - optional royalties information for these clones
562/// * `memo` - optional memo for the mint txs
563#[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    // if mint_list did not error, there must be at least one token id
624    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
645/// Returns HandleResult
646///
647/// sets new public and/or private metadata
648///
649/// # Arguments
650///
651/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
652/// * `env` - Env of contract's environment
653/// * `config` - a reference to the Config
654/// * `priority` - u8 representation of highest status level this action is permitted at
655/// * `token_id` - token id String slice of token whose metadata should be updated
656/// * `public_metadata` - the optional new public metadata viewable by everyone
657/// * `private_metadata` - the optional new private metadata viewable by everyone
658pub 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    // if token supply is private, don't leak that the token id does not exist
670    // instead just say they are not authorized for that token
671    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
697/// Returns HandleResult
698///
699/// sets new royalty information for a specified token or if no token ID is provided, sets new
700/// royalty information as the contract's default
701///
702/// # Arguments
703///
704/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
705/// * `env` - Env of contract's environment
706/// * `config` - a reference to the Config
707/// * `priority` - u8 representation of highest status level this action is permitted at
708/// * `token_id` - optional token id String slice of token whose royalty info should be updated
709/// * `royalty_info` - a optional reference to the new RoyaltyInfo
710pub 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    // set a token's royalties
721    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        // if token supply is private, don't leak that the token id does not exist
724        // instead just say they are not authorized for that token
725        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    // set default royalty
755    } 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
780/// Returns HandleResult
781///
782/// makes the sealed private metadata public
783///
784/// # Arguments
785///
786/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
787/// * `env` - Env of contract's environment
788/// * `config` - a reference to the Config
789/// * `priority` - u8 representation of highest status level this action is permitted at
790/// * `token_id` - token id String slice of token whose metadata should be updated
791pub 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    // if token supply is private, don't leak that the token id does not exist
807    // instead just say they do not own that token
808    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/// Returns HandleResult
843///
844/// grants/revokes trasfer permission on a token
845///
846/// # Arguments
847///
848/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
849/// * `env` - Env of contract's environment
850/// * `config` - a reference to the Config
851/// * `priority` - u8 representation of highest status level this action is permitted at
852/// * `spender` - a reference to the address being granted permission
853/// * `token_id` - string slice of the token id to grant permission to
854/// * `expires` - optional Expiration for this approval
855/// * `is_approve` - true if this is an Approve call
856#[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    // if token supply is private, don't leak that the token id does not exist
875    // instead just say they are not authorized for that token
876    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 not called by the owner, check if message sender has operator status
886    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
942/// Returns HandleResult
943///
944/// makes an address' token ownership private
945///
946/// # Arguments
947///
948/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
949/// * `env` - Env of contract's environment
950/// * `config` - a reference to the Config
951/// * `priority` - u8 representation of highest status level this action is permitted at
952pub 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    // only need to do this if the contract has public ownership
961    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/// Returns HandleResult
975///
976/// adds/revokes access for everyone
977///
978/// # Arguments
979///
980/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
981/// * `env` - Env of contract's environment
982/// * `config` - a reference to the Config
983/// * `priority` - u8 representation of highest status level this action is permitted at
984/// * `token_id` - optional token id to apply approvals to
985/// * `view_owner` - optional access level for viewing token ownership
986/// * `view_private_metadata` - optional access level for viewing private metadata
987/// * `expires` - optional Expiration for this approval
988#[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    // use this "address" to represent global permission
1002    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        // if token supply is private, don't leak that the token id does not exist
1009        // instead just say they do not own that token
1010        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 trying to set token permissions when you are not the owner
1029    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/// Returns HandleResult
1061///
1062/// sets specified permissions for an address
1063///
1064/// # Arguments
1065///
1066/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1067/// * `env` - Env of contract's environment
1068/// * `config` - a reference to the Config
1069/// * `priority` - u8 representation of highest status level this action is permitted at
1070/// * `address` - a reference to the address being granted permission
1071/// * `token_id` - optional token id to apply approvals to
1072/// * `view_owner` - optional access level for viewing token ownership
1073/// * `view_private_metadata` - optional access level for viewing private metadata
1074/// * `transfer` - optional access level for transferring tokens
1075/// * `expires` - optional Expiration for this approval
1076/// * `response_type` - which response to return for SetWhitelistedApproval, ApproveAll, or RevokeAll
1077#[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        // if token supply is private, don't leak that the token id does not exist
1100        // instead just say they do not own that token
1101        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 trying to set token permissions when you are not the owner
1120    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
1158/// Returns HandleResult
1159///
1160/// burns many tokens
1161///
1162/// # Arguments
1163///
1164/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1165/// * `env` - Env of contract's environment
1166/// * `config` - a mutable reference to the Config
1167/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1168/// * `burns` - the list of burns to perform
1169pub 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
1187/// Returns HandleResult
1188///
1189/// burns a token
1190///
1191/// # Arguments
1192///
1193/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1194/// * `env` - Env of contract's environment
1195/// * `config` - a mutable reference to the Config
1196/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1197/// * `token_id` - token id String of token to be burnt
1198/// * `memo` - optional memo for the burn tx
1199fn 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
1222/// Returns HandleResult
1223///
1224/// transfer many tokens
1225///
1226/// # Arguments
1227///
1228/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1229/// * `env` - Env of contract's environment
1230/// * `config` - a mutable reference to the Config
1231/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1232/// * `transfers` - list of transfers to perform
1233pub 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
1254/// Returns HandleResult
1255///
1256/// transfer a token
1257///
1258/// # Arguments
1259///
1260/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1261/// * `env` - Env of contract's environment
1262/// * `config` - a mutable reference to the Config
1263/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1264/// * `recipient` - the address receiving the token
1265/// * `token_id` - token id String of token to be transferred
1266/// * `memo` - optional memo for the mint tx
1267pub 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
1293/// Returns HandleResult
1294///
1295/// sends tokens to contracts, and calls those contracts' ReceiveNft.  Will error if any
1296/// contract has not registered its ReceiveNft
1297///
1298/// # Arguments
1299///
1300/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1301/// * `env` - Env of contract's environment
1302/// * `config` - a mutable reference to the Config
1303/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1304/// * `sends` - list of SendNfts to perform
1305fn 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/// Returns HandleResult
1325///
1326/// sends a token to a contract, and calls that contract's ReceiveNft.  Will error if the
1327/// contract has not registered its ReceiveNft
1328///
1329/// # Arguments
1330///
1331/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1332/// * `env` - Env of contract's environment
1333/// * `config` - a mutable reference to the Config
1334/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1335/// * `contract` - the address of the contract receiving the token
1336/// * `receiver_info` - optional code hash and BatchReceiveNft implementation status of
1337///                     the recipient contract
1338/// * `token_id` - ID String of the token that was sent
1339/// * `msg` - optional msg used to control ReceiveNft logic
1340/// * `memo` - optional memo for the mint tx
1341#[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
1372/// Returns HandleResult
1373///
1374/// registers a contract's ReceiveNft
1375///
1376/// # Arguments
1377///
1378/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1379/// * `env` - Env of contract's environment
1380/// * `config` - a reference to the Config
1381/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1382/// * `code_hash` - code hash String of the registering contract
1383/// * `impl_batch` - optionally true if the contract also implements BatchReceiveNft
1384pub 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(), &regrec)?;
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
1410/// Returns HandleResult
1411///
1412/// creates a viewing key
1413///
1414/// # Arguments
1415///
1416/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1417/// * `env` - Env of contract's environment
1418/// * `config` - a reference to the Config
1419/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1420/// * `entropy` - string slice of the input String to be used as entropy in randomization
1421pub 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
1443/// Returns HandleResult
1444///
1445/// sets the viewing key to the input String
1446///
1447/// # Arguments
1448///
1449/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1450/// * `env` - Env of contract's environment
1451/// * `config` - a reference to the Config
1452/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1453/// * `key` - String to be used as the viewing key
1454pub 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
1473/// Returns HandleResult
1474///
1475/// add a list of minters
1476///
1477/// # Arguments
1478///
1479/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1480/// * `env` - Env of contract's environment
1481/// * `config` - a reference to the Config
1482/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1483/// * `new_minters` - list of minter addresses to add
1484pub 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    // only save if the list changed
1507    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
1517/// Returns HandleResult
1518///
1519/// remove a list of minters
1520///
1521/// # Arguments
1522///
1523/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1524/// * `env` - Env of contract's environment
1525/// * `config` - a reference to the Config
1526/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1527/// * `no_minters` - list of minter addresses to remove
1528pub 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
1566/// Returns HandleResult
1567///
1568/// define the exact list of minters
1569///
1570/// # Arguments
1571///
1572/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1573/// * `env` - Env of contract's environment
1574/// * `config` - a reference to the Config
1575/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1576/// * `human_minters` - exact list of minter addresses
1577pub 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    // remove duplicates from the minters list
1592    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
1615/// Returns HandleResult
1616///
1617/// change the admin address
1618///
1619/// # Arguments
1620///
1621/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1622/// * `env` - Env of contract's environment
1623/// * `config` - a mutable reference to the Config
1624/// * `priority` - u8 representation of highest ContractStatus level this action is permitted
1625/// * `address` - new admin address
1626pub 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
1652/// Returns HandleResult
1653///
1654/// set the contract status level
1655///
1656/// # Arguments
1657///
1658/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
1659/// * `env` - Env of contract's environment
1660/// * `config` - a mutable reference to the Config
1661/// * `level` - new ContractStatus
1662pub 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
1688/// Returns HandleResult
1689///
1690/// revoke the ability to use a specified permit
1691///
1692/// # Arguments
1693///
1694/// * `storage` - mutable reference to the contract's storage
1695/// * `sender` - a reference to the message sender
1696/// * `permit_name` - string slice of the name of the permit to revoke
1697fn 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
1711/////////////////////////////////////// Query /////////////////////////////////////
1712/// Returns QueryResult
1713///
1714/// # Arguments
1715///
1716/// * `deps` - reference to Extern containing all the contract's external dependencies
1717/// * `msg` - QueryMsg passed in with the query call
1718pub 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
1827/// Returns QueryResult from validating a permit and then using its creator's address when
1828/// performing the specified query
1829///
1830/// # Arguments
1831///
1832/// * `deps` - a reference to Extern containing all the contract's external dependencies
1833/// * `permit` - the permit used to authentic the query
1834/// * `query` - the query to perform
1835pub fn permit_queries<S: Storage, A: Api, Q: Querier>(
1836    deps: &Extern<S, A, Q>,
1837    permit: Permit,
1838    query: QueryWithPermit,
1839) -> QueryResult {
1840    // Validate permit content
1841    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    // permit validated, process query
1858    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
1912/// Returns QueryResult displaying the contract's creator
1913///
1914/// # Arguments
1915///
1916/// * `deps` - a reference to Extern containing all the contract's external dependencies
1917pub 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
1926/// Returns QueryResult displaying the contract's name and symbol
1927///
1928/// # Arguments
1929///
1930/// * `storage` - a reference to the contract's storage
1931pub 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
1940/// Returns QueryResult displaying either a token's royalty information or the contract's
1941/// default royalty information if no token_id is specified
1942///
1943/// # Arguments
1944///
1945/// * `deps` - a reference to Extern containing all the contract's external dependencies
1946/// * `token_id` - optional token id whose RoyaltyInfo is being requested
1947/// * `viewer` - optional address and key making an authenticated query request
1948/// * `from_permit` - address derived from an Owner permit, if applicable
1949pub 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        // TODO remove this when BlockInfo becomes available to queries
1958        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 the token id was found
1964        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            // get the royalty information if present
1978            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        // token id not found
1984        } 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 minter querying or the token supply is public, let them know the token does not exist
1990            if config.token_supply_is_public || is_minter {
1991                return Err(StdError::generic_err(format!("Token ID: {} not found", id)));
1992            }
1993            // token supply is private and querier is not a minter so just show the default without addresses
1994            (
1995                may_load::<StoredRoyaltyInfo, _>(&deps.storage, DEFAULT_ROYALTY_KEY)?,
1996                true,
1997            )
1998        }
1999    // no id specified, so get the default
2000    } else {
2001        let minters: Vec<CanonicalAddr> = may_load(&deps.storage, MINTERS_KEY)?.unwrap_or_default();
2002        // only let minters view default royalty addresses
2003        (
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
2015/// Returns QueryResult displaying the contract's configuration
2016///
2017/// # Arguments
2018///
2019/// * `storage` - a reference to the contract's storage
2020pub 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
2034/// Returns QueryResult displaying the list of authorized minters
2035///
2036/// # Arguments
2037///
2038/// * `deps` - a reference to Extern containing all the contract's external dependencies
2039pub 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
2050/// Returns QueryResult displaying the number of tokens the contract controls
2051///
2052/// # Arguments
2053///
2054/// * `deps` - a reference to Extern containing all the contract's external dependencies
2055/// * `viewer` - optional address and key making an authenticated query request
2056/// * `from_permit` - address derived from an Owner permit, if applicable
2057pub 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    // authenticate permission to view token supply
2063    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
2070/// Returns QueryResult displaying the list of tokens that the contract controls
2071///
2072/// # Arguments
2073///
2074/// * `deps` - a reference to Extern containing all the contract's external dependencies
2075/// * `viewer` - optional address and key making an authenticated query request
2076/// * `start_after` - optionally only display token ids that come after this one
2077/// * `limit` - optional max number of tokens to display
2078/// * `from_permit` - address derived from an Owner permit, if applicable
2079pub 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    // authenticate permission to view token supply
2087    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            // will hit gas ceiling before the count overflows
2108            count += 1;
2109        }
2110        // i can't overflow if it was less than a u32
2111        i += 1;
2112    }
2113    to_binary(&QueryAnswer::TokenList { tokens })
2114}
2115
2116/// Returns QueryResult displaying the owner of the input token if the requester is authorized
2117/// to view it and the transfer approvals on this token if the owner is querying
2118///
2119/// # Arguments
2120///
2121/// * `deps` - a reference to Extern containing all the contract's external dependencies
2122/// * `token_id` - string slice of the token id
2123/// * `viewer` - optional address and key making an authenticated query request
2124/// * `include_expired` - optionally true if the Approval lists should include expired Approvals
2125/// * `from_permit` - address derived from an Owner permit, if applicable
2126pub 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
2144/// Returns QueryResult displaying the public metadata of a token
2145///
2146/// # Arguments
2147///
2148/// * `storage` - a reference to the contract's storage
2149/// * `token_id` - string slice of the token id
2150pub 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 token id was found
2154    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    // token id wasn't found
2167    // if the token supply is public, let them know the token does not exist
2168    if config.token_supply_is_public {
2169        return Err(StdError::generic_err(format!(
2170            "Token ID: {} not found",
2171            token_id
2172        )));
2173    }
2174    // otherwise, just return empty metadata
2175    to_binary(&QueryAnswer::NftInfo {
2176        token_uri: None,
2177        extension: None,
2178    })
2179}
2180
2181/// Returns QueryResult displaying the private metadata of a token if permitted to
2182/// view it
2183///
2184/// # Arguments
2185///
2186/// * `deps` - a reference to Extern containing all the contract's external dependencies
2187/// * `token_id` - string slice of the token id
2188/// * `viewer` - optional address and key making an authenticated query request
2189/// * `from_permit` - address derived from an Owner permit, if applicable
2190pub 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    // don't display if private metadata is sealed
2209    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
2225/// Returns QueryResult displaying response of both the OwnerOf and NftInfo queries
2226///
2227/// # Arguments
2228///
2229/// * `deps` - a reference to Extern containing all the contract's external dependencies
2230/// * `token_id` - string slice of the token id
2231/// * `viewer` - optional address and key making an authenticated query request
2232/// * `include_expired` - optionally true if the Approval lists should include expired Approvals
2233/// * `from_permit` - address derived from an Owner permit, if applicable
2234pub 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
2249/// Returns QueryResult displaying all the token information the querier is permitted to
2250/// view.  This may include the owner, the public metadata, the private metadata, royalty
2251/// information, mint run information, whether the token is unwrapped, whether the token is
2252/// transferable, and the token and inventory approvals
2253///
2254/// # Arguments
2255///
2256/// * `deps` - a reference to Extern containing all the contract's external dependencies
2257/// * `token_id` - the token id
2258/// * `viewer` - optional address and key making an authenticated query request
2259/// * `include_expired` - optionally true if the Approval lists should include expired Approvals
2260/// * `from_permit` - address derived from an Owner permit, if applicable
2261pub 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
2292/// Returns QueryResult displaying all the token information the querier is permitted to
2293/// view of multiple tokens.  This may include the owner, the public metadata, the private metadata,
2294/// royalty information, mint run information, whether the token is unwrapped, whether the token is
2295/// transferable, and the token and inventory approvals
2296///
2297/// # Arguments
2298///
2299/// * `deps` - a reference to Extern containing all the contract's external dependencies
2300/// * `token_ids` - list of token ids whose info should be retrieved
2301/// * `viewer` - optional address and key making an authenticated query request
2302/// * `include_expired` - optionally true if the Approval lists should include expired Approvals
2303/// * `from_permit` - address derived from an Owner permit, if applicable
2304pub 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
2316/// Returns QueryResult displaying the approvals in place for a specified token
2317///
2318/// # Arguments
2319///
2320/// * `deps` - a reference to Extern containing all the contract's external dependencies
2321/// * `token_id` - string slice of the token id
2322/// * `viewing_key` - the optional token owner's viewing key String
2323/// * `include_expired` - optionally true if the Approval lists should include expired Approvals
2324/// * `from_permit` - address derived from an Owner permit, if applicable
2325pub 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    // if token supply is private, don't leak that the token id does not exist
2338    // instead just say they are not authorized for that token
2339    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    // verify that the querier is the token owner
2346    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    // TODO remove this when BlockInfo becomes available to queries
2361    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    // determine if ownership is public
2385    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    // determine if private metadata is public
2393    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
2408/// Returns QueryResult displaying the inventory-wide approvals for a specified address
2409///
2410/// # Arguments
2411///
2412/// * `deps` - a reference to Extern containing all the contract's external dependencies
2413/// * `viewer` - optional address and key making an authenticated query request
2414/// * `include_expired` - optionally true if the Approval lists should include expired Approvals
2415/// * `from_permit` - address derived from an Owner permit, if applicable
2416pub 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    // TODO remove this when BlockInfo becomes available to queries
2431    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
2471/// Returns QueryResult displaying the list of all addresses that have approval to transfer
2472/// all of the owner's tokens.  Only the owner's viewing key will be accepted for this query
2473///
2474/// # Arguments
2475///
2476/// * `deps` - a reference to Extern containing all the contract's external dependencies
2477/// * `owner` - an optional reference to the address whose transfer ALL list should be displayed
2478/// * `viewing_key` - optional String of the owner's viewing key
2479/// * `include_expired` - optionally true if the Approval list should include expired Approvals
2480/// * `from_permit` - address derived from an Owner permit, if applicable
2481pub 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    // get the address whose approvals are being queried
2489    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        // didn't supply a viewing key so just return an empty list of approvals
2498        } else {
2499            return to_binary(&QueryAnswer::ApprovedForAll {
2500                operators: Vec::new(),
2501            });
2502        }
2503        raw
2504    };
2505    // TODO remove this when BlockInfo becomes available to queries
2506    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
2527/// Returns QueryResult displaying an optionally paginated list of all tokens belonging to
2528/// the owner address.  It will only display the tokens that the querier has view_owner
2529/// approval
2530///
2531/// # Arguments
2532///
2533/// * `deps` - a reference to Extern containing all the contract's external dependencies
2534/// * `owner` - a reference to the address whose tokens should be displayed
2535/// * `viewer` - optional address of the querier if different from the owner
2536/// * `viewing_key` - optional viewing key String
2537/// * `start_after` - optionally only display token ids that come after this String in
2538///                   lexicographical order
2539/// * `limit` - optional max number of tokens to display
2540/// * `from_permit` - address derived from an Owner permit, if applicable
2541pub 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    // determine the querier
2553    let (is_owner, may_querier) = if let Some(pmt) = from_permit.as_ref() {
2554        // permit tells you who is querying, so also check if he is the owner
2555        (owner_raw == *pmt, from_permit)
2556    // no permit, so check if a key was provided and who it matches
2557    } else if let Some(key) = viewing_key {
2558        // if there is a viewer
2559        viewer
2560            // convert to canonical
2561            .map(|v| deps.api.canonical_address(&v))
2562            .transpose()?
2563            // only keep the viewer address if the viewing key matches
2564            .filter(|v| check_key(&deps.storage, v, key.clone()).is_ok())
2565            .map_or_else(
2566                // no viewer or key did not match
2567                || {
2568                    // check if the key matches the owner, and error if it fails this last chance
2569                    check_key(&deps.storage, &owner_raw, key)?;
2570                    Ok((true, Some(owner_raw.clone())))
2571                },
2572                // we know the querier is the viewer, so check if someone put the same address for both
2573                |v| Ok((v == owner_raw, Some(v))),
2574            )?
2575    // no permit, no viewing key, so querier is unknown
2576    } else {
2577        (false, None)
2578    };
2579    // exit early if the limit is 0
2580    if cut_off == 0 {
2581        return to_binary(&QueryAnswer::TokenList { tokens: Vec::new() });
2582    }
2583    // get list of owner's tokens
2584    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    // if querier is different than the owner, check if ownership is public
2589    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    // TODO remove this when BlockInfo becomes available to queries
2600    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        // load the config if we haven't already
2622        let config = may_config.map_or_else(|| load::<Config, _>(&deps.storage, CONFIG_KEY), Ok)?;
2623        // if the querier is allowed to view all of the owner's tokens, let them know if the token
2624        // does not belong to the owner
2625        let inv_err = format!("Token ID: {} is not in the specified inventory", after);
2626        // or tell any other viewer that they are not authorized
2627        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        // if token supply is public let them know if the token id does not exist
2633        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        // make sure querier is allowed to know if the supplied token belongs to owner
2644        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            // if the specified token belongs to the specified owner, save if the querier is an operator
2648            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 querier is found to have ALL permission for the specified owner, no need to check permission ever again
2665            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            // only check permissions if not public or owner
2678            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 querier is found to have ALL permission, no need to check permission ever again
2693                    if !oper_for.is_empty() {
2694                        known_pass = true;
2695                    }
2696                }
2697            }
2698            if list_it {
2699                tokens.push(id);
2700                // it'll hit the gas ceiling before overflowing the count
2701                count += 1;
2702                // exit if we hit the limit
2703                if count >= cut_off {
2704                    break;
2705                }
2706            }
2707        }
2708    }
2709    to_binary(&QueryAnswer::TokenList { tokens })
2710}
2711
2712/// Returns QueryResult displaying the number of tokens that the querier has permission to
2713/// view ownership and that belong to the specified address
2714///
2715/// # Arguments
2716///
2717/// * `deps` - a reference to Extern containing all the contract's external dependencies
2718/// * `owner` - a reference to the address whose tokens should be displayed
2719/// * `viewer` - optional address of the querier if different from the owner
2720/// * `viewing_key` - optional viewing key String
2721/// * `from_permit` - address derived from an Owner permit, if applicable
2722pub 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    // determine the querier
2731    let (is_owner, may_querier) = if let Some(pmt) = from_permit.as_ref() {
2732        // permit tells you who is querying, so also check if he is the owner
2733        (owner_raw == *pmt, from_permit)
2734    // no permit, so check if a key was provided and who it matches
2735    } else if let Some(key) = viewing_key {
2736        // if there is a viewer
2737        viewer
2738            // convert to canonical
2739            .map(|v| deps.api.canonical_address(&v))
2740            .transpose()?
2741            // only keep the viewer address if the viewing key matches
2742            .filter(|v| check_key(&deps.storage, v, key.clone()).is_ok())
2743            .map_or_else(
2744                // no viewer or key did not match
2745                || {
2746                    // check if the key matches the owner, and error if it fails this last chance
2747                    check_key(&deps.storage, &owner_raw, key)?;
2748                    Ok((true, Some(owner_raw.clone())))
2749                },
2750                // we know the querier is the viewer, so check if someone put the same address for both
2751                |v| Ok((v == owner_raw, Some(v))),
2752            )?
2753    // no permit, no viewing key, so querier is unknown
2754    } else {
2755        (false, None)
2756    };
2757
2758    // get list of owner's tokens
2759    let own_inv = Inventory::new(&deps.storage, owner_raw)?;
2760    let owner_slice = own_inv.owner.as_slice();
2761
2762    // if querier is different than the owner, check if ownership is public
2763    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    // TODO remove this when BlockInfo becomes available to queries
2772    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        // check if the ownership has been made public or the sender has ALL permission.
2796        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                    // we can quit if we found both the sender and the global (or if only searching for public)
2808                    if found_one {
2809                        break;
2810                    } else {
2811                        found_one = true;
2812                    }
2813                }
2814            }
2815        }
2816    }
2817    // if it is either the owner, ownership is public, or the querier has inventory-wide view owner permission,
2818    // let them see the full count
2819    if known_pass {
2820        return to_binary(&QueryAnswer::NumTokens {
2821            count: own_inv.info.count,
2822        });
2823    }
2824
2825    // get the list of tokens that might have viewable ownership for this querier
2826    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    // check if the the token permissions have expired, and if not include it in the count
2841    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                    // we can quit if we found both the sender and the global (or if only searching for public)
2855                    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
2868/// Returns QueryResult displaying true if the token has been unwrapped.  If sealed metadata
2869/// is not enabled, all tokens are considered unwrapped
2870///
2871/// # Arguments
2872///
2873/// * `storage` - a reference to the contract's storage
2874pub 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            // if the token id is not found, but token supply is private, just say
2880            // the token's wrapped state is the same as a newly minted token
2881            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
2896/// Returns QueryResult displaying true if the token is transferable
2897///
2898/// # Arguments
2899///
2900/// * `storage` - a reference to the contract's storage
2901pub 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            // if the token id is not found, but token supply is private, just say
2907            // the token is transferable
2908            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
2923/// Returns QueryResult displaying an optionally paginated list of all transactions
2924/// involving a specified address, displayed in reverse chronological order
2925///
2926/// # Arguments
2927///
2928/// * `deps` - a reference to Extern containing all the contract's external dependencies
2929/// * `viewer` - optional address and key making an authenticated query request
2930/// * `page` - an optional page number.  If given, the most recent `page` times `page_size`
2931///            transactions will be skipped
2932/// * `page_size` - optional max number of transactions to display
2933/// * `from_permit` - address derived from an Owner permit, if applicable
2934pub 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
2954/// Returns QueryResult after verifying that the specified address has transfer approval
2955/// for all the listed tokens.  A token will count as unapproved if it is non-transferable
2956///
2957/// # Arguments
2958///
2959/// * `deps` - a reference to Extern containing all the contract's external dependencies
2960/// * `token_ids` - a list of token ids to check if the address has transfer approval
2961/// * `viewer` - optional address and key making an authenticated query request
2962/// * `from_permit` - address derived from an Owner permit, if applicable
2963pub 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    // TODO remove this when BlockInfo becomes available to queries
2974    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        // cargo fmt creates the and_then block, but clippy doesn't like it
2982        #[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            // the and_then forces an error if the token is not transferable
2992        )
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
3014/// Returns QueryResult displaying the registered code hash of the specified contract if
3015/// it has registered and whether the contract implements BatchReceiveNft
3016///
3017/// # Arguments
3018///
3019/// * `deps` - a reference to Extern containing all the contract's external dependencies
3020/// * `contract` - a reference to the contract's address whose code hash is being requested
3021pub 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
3040// bundled info when prepping an authenticated token query
3041pub struct TokenQueryInfo {
3042    // querier's address
3043    viewer_raw: Option<CanonicalAddr>,
3044    // TODO remove this when BlockInfo becomes available to queries
3045    block: BlockInfo,
3046    // error message String
3047    err_msg: String,
3048    // the requested token
3049    token: Token,
3050    // the requested token's index
3051    idx: u32,
3052    // true if the contract has public ownership
3053    owner_is_public: bool,
3054}
3055
3056/// Returns StdResult<TokenQueryInfo> after performing common preparations for authenticated
3057/// token queries
3058///
3059/// # Arguments
3060///
3061/// * `deps` - a reference to Extern containing all the contract's external dependencies
3062/// * `token_id` - string slice of the token id
3063/// * `viewer` - optional address and key making an authenticated query request
3064/// * `from_permit` - address derived from an Owner permit, if applicable
3065fn 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    // TODO remove this when BlockInfo becomes available to queries
3074    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    // if token supply is private, don't leak that the token id does not exist
3084    // instead just say they are not authorized for that token
3085    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
3101/// Returns StdResult<(Option<HumanAddr>, Vec<Cw721Approval>, u32)> which is the owner, list of transfer
3102/// approvals, and token index of the request token
3103///
3104/// # Arguments
3105///
3106/// * `deps` - a reference to Extern containing all the contract's external dependencies
3107/// * `token_id` - string slice of the token id
3108/// * `viewer` - optional address and key making an authenticated query request
3109/// * `include_expired` - optionally true if the Approval lists should include expired Approvals
3110/// * `from_permit` - address derived from an Owner permit, if applicable
3111fn 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
3146/// Returns StdResult<(HumanAddr, Vec<Cw721Approval>, Vec<Cw721Approval>)>
3147/// which is the owner, token transfer Approval list, and Approval list of everyone
3148/// that can transfer all of the token owner's tokens
3149///
3150/// # Arguments
3151///
3152/// * `deps` - a reference to Extern containing all the contract's external dependencies
3153/// * `block` - a reference to the current BlockInfo
3154/// * `token` - a reference to the token whose owner info is being requested
3155/// * `viewer` - optional reference to the address requesting to view the owner
3156/// * `include_expired` - true if the Approval lists should include expired Approvals
3157fn 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
3194/// Returns StdResult<()> resulting from generating the list of Approvals
3195///
3196/// # Arguments
3197///
3198/// * `api` - reference to the Api used to convert canonical and human addresses
3199/// * `block` - a reference to the current BlockInfo
3200/// * `perm_list` - slice of Permissions to search through looking for transfer approvals
3201/// * `approvals` - a mutable reference to the list of approvals that should be appended
3202///                 with any found in the permission list
3203/// * `transfer_idx` - index into the Permission expirations that represents transfers
3204/// * `include_expired` - true if the Approval list should include expired Approvals
3205fn 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
3227// permission type info
3228pub struct PermissionTypeInfo {
3229    // index for view owner permission
3230    pub view_owner_idx: usize,
3231    // index for view private metadata permission
3232    pub view_meta_idx: usize,
3233    // index for transfer permission
3234    pub transfer_idx: usize,
3235    // number of permission types
3236    pub num_types: usize,
3237}
3238
3239/// Returns StdResult<(Vec<Snip721Approval>, Option<Expiration>, Option<Expiration>)>
3240/// which is the list of approvals, an optional Expiration if ownership is public, and
3241/// an optional Expiration if the private metadata is public
3242///
3243/// # Arguments
3244///
3245/// * `api` - reference to the Api used to convert canonical and human addresses
3246/// * `block` - a reference to the current BlockInfo
3247/// * `perm_list` - a mutable reference to the list of Permission
3248/// * `include_expired` - true if the Approval list should include expired Approvals
3249/// * `perm_type_info` - a reference to PermissionTypeInfo
3250fn 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        // set global permissions if present
3263        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        // otherwise create the approval summary
3275        } 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
3299/// Returns StdResult<()>
3300///
3301/// returns Ok if authorized to view token supply, Err otherwise
3302///
3303/// # Arguments
3304///
3305/// * `deps` - a reference to Extern containing all the contract's external dependencies
3306/// * `viewer` - optional address and key making an authenticated query request
3307/// * `from_permit` - address derived from an Owner permit, if applicable
3308fn 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
3331/// Returns StdResult<bool> result of validating an address' viewing key
3332///
3333/// # Arguments
3334///
3335/// * `storage` - a reference to the contract's storage
3336/// * `address` - a reference to the address whose key should be validated
3337/// * `viewing_key` - String key used for authentication
3338fn check_key<S: ReadonlyStorage>(
3339    storage: &S,
3340    address: &CanonicalAddr,
3341    viewing_key: String,
3342) -> StdResult<()> {
3343    // load the address' key
3344    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 key matches
3349    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/// Returns StdResult<()>
3358///
3359/// returns Ok if the address has permission or an error if not
3360///
3361/// # Arguments
3362///
3363/// * `deps` - a reference to Extern containing all the contract's external dependencies
3364/// * `block` - a reference to the current BlockInfo
3365/// * `token` - a reference to the token
3366/// * `token_id` - token ID String slice
3367/// * `opt_sender` - a optional reference to the address trying to get access to the token
3368/// * `perm_type` - PermissionType we are checking
3369/// * `oper_for` - a mutable reference to a list of owners that gave the sender "all" permission
3370/// * `custom_err` - string slice of the error msg to return if not permitted
3371/// * `owner_is_public` - true if token ownership is public for this contract
3372#[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    // check if owner is public/private.  use owner's setting if present, contract default
3387    // if not
3388    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/// Returns StdResult<()>
3409///
3410/// returns Ok if the address has permission or an error if not
3411///
3412/// # Arguments
3413///
3414/// * `deps` - a reference to Extern containing all the contract's external dependencies
3415/// * `block` - a reference to the current BlockInfo
3416/// * `token` - a reference to the token
3417/// * `token_id` - token ID String slice
3418/// * `opt_sender` - a optional reference to the address trying to get access to the token
3419/// * `owner_slice` - the owner of the token represented as a byte slice
3420/// * `exp_idx` - permission type we are checking represented as usize
3421/// * `oper_for` - a mutable reference to a list of owners that gave the sender "all" permission
3422/// * `custom_err` - string slice of the error msg to return if not permitted
3423#[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 did not already pass with "all" permission for this owner
3436    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 this is the owner, all is good
3446        if token.owner == *sender {
3447            return Ok(());
3448        }
3449        // check if the token is public or the sender has token permission.
3450        // Can't use find because even if the global or sender permission expired, you
3451        // still want to see if the other is still valid, but if we are only checking for public
3452        // we can quit after one failure
3453        let mut one_expired = only_public;
3454        // if we are only checking for public permission, we can quit after one failure
3455        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                    // if the permission is expired
3462                    } else {
3463                        // if this is the sender let them know the permission expired
3464                        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 both were expired (or only checking for global), there can't be any ALL permissions
3470                        // so just exit early
3471                        if one_expired {
3472                            return Err(StdError::generic_err(err_msg));
3473                        } else {
3474                            one_expired = true;
3475                        }
3476                    }
3477                }
3478                // we can quit if we found both the sender and the global (or only checking global)
3479                if found_one {
3480                    break;
3481                } else {
3482                    found_one = true;
3483                }
3484            }
3485        }
3486        // check if the entire permission type is public or the sender has ALL permission.
3487        // Can't use find because even if the global or sender permission expired, you
3488        // still want to see if the other is still valid, but if we are only checking for public
3489        // we can quit after one failure
3490        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                        // if the permission expired and this is the sender let them know the
3501                        // permission expired
3502                        } 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                    // we can quit if we found both the sender and the global (or only checking global)
3511                    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
3524/// Returns StdResult<(Token, u32)>
3525///
3526/// returns the token information if the sender has authorization
3527///
3528/// # Arguments
3529///
3530/// * `deps` - a reference to Extern containing all the contract's external dependencies
3531/// * `block` - a reference to the current BlockInfo
3532/// * `token_id` - token ID String slice
3533/// * `sender` - a optional reference to the address trying to get access to the token
3534/// * `perm_type` - PermissionType we are checking
3535/// * `oper_for` - a mutable reference to a list of owners that gave the sender "all" permission
3536/// * `config` - a reference to the Config
3537fn 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    // if token supply is private, don't leak that the token id does not exist
3551    // instead just say they are not authorized for that token
3552    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
3572/// Returns StdResult<(Token, u32)>
3573///
3574/// returns the specified token and its identifier index
3575///
3576/// # Arguments
3577///
3578/// * `storage` - a reference to contract's storage
3579/// * `token_id` - token id string slice
3580/// * `custom_err` - optional custom error message to use if don't want to reveal that a token
3581///                  does not exist
3582fn 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
3604/// Returns StdResult<()> that will error if the priority level of the action is not
3605/// equal to or greater than the current contract status level
3606///
3607/// # Arguments
3608///
3609/// * `contract_status` - u8 representation of the current contract status
3610/// * `priority` - u8 representing the highest status level this action may execute at
3611fn 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/// Returns StdResult<()>
3621///
3622/// sets new metadata
3623///
3624/// # Arguments
3625///
3626/// * `storage` - a mutable reference to the contract's storage
3627/// * `token` - a reference to the token whose metadata should be updated
3628/// * `idx` - the token identifier index
3629/// * `prefix` - storage prefix for the type of metadata being updated
3630/// * `metadata` - a reference to the new metadata
3631#[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    // do not allow the altering of sealed metadata
3640    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
3651// enum used to return correct response from SetWhitelistedApproval
3652pub enum SetAppResp {
3653    SetWhitelistedApproval,
3654    ApproveAll,
3655    RevokeAll,
3656}
3657
3658// table of bools used to alter AuthLists properly
3659#[derive(Default)]
3660pub struct AlterAuthTable {
3661    // true if the specified token index should be added to an AuthList for that PermissionType
3662    pub add: [bool; 3],
3663    // true if all but the specified token index should be added to an AuthList for that PermType
3664    pub full: [bool; 3],
3665    // true if the specified token index should be removed from an AuthList for that PermType
3666    pub remove: [bool; 3],
3667    // true if the AuthList should be cleared for that Permission Type
3668    pub clear: [bool; 3],
3669    // true if there is at least one true in the table
3670    pub has_update: bool,
3671}
3672
3673// table of bools used to alter a permission list appropriately
3674#[derive(Default)]
3675pub struct AlterPermTable {
3676    // true if the address should be added to the permission list for that PermissionType
3677    pub add: [bool; 3],
3678    // true if the address should be removed from the permission list for that PermissionType
3679    pub remove: [bool; 3],
3680    // true if there is at least one true in the table
3681    pub has_update: bool,
3682}
3683
3684// bundled info needed when setting accesses
3685pub struct ProcessAccInfo {
3686    // the input token or a default
3687    pub token: Token,
3688    // input token's mint index or a default
3689    pub idx: u32,
3690    // true if there was an input token
3691    pub token_given: bool,
3692    // the accesses being set
3693    pub accesses: [Option<AccessLevel>; 3],
3694    // optional expiration
3695    pub expires: Option<Expiration>,
3696    // true if this is an operator trying to set permissions
3697    pub from_oper: bool,
3698}
3699
3700/// Returns StdResult<()>
3701///
3702/// sets specified permissions for an address
3703///
3704/// # Arguments
3705///
3706/// * `storage` - a mutable reference to the contract's storage
3707/// * `env` - a reference to the Env of the contract's environment
3708/// * `address` - a reference to the address being granted/revoked permission
3709/// * `owner` - a reference to the permission owner's address
3710/// * `proc_info` - a mutable reference to the ProcessAccInfo
3711/// * `all_perm_in` - when from an operator, the all_perms have already been read
3712fn 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    // do every permission type
3741    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                    // load the "all" permissions if we haven't already and see if the address is there
3752                    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 this address has "all" permission
3765                    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                                    // don't allow one operator to change to another
3770                                    // operator's permissions
3771                                    if proc_info.from_oper {
3772                                        // if adding, don't do anything
3773                                        if is_approve {
3774                                            return Ok(());
3775                                        // if revoking, throw error
3776                                        } else {
3777                                            return Err(StdError::generic_err(
3778                                                "Can not revoke transfer permission from an existing operator",
3779                                            ));
3780                                        }
3781                                    }
3782                                    // if you are granting token approval to an existing
3783                                    // operator, but not changing the expiration, nothing
3784                                    // needs to be done
3785                                    if is_approve && expirations[i] == exp {
3786                                        return Ok(());
3787                                    }
3788                                    // need to put all the other tokens in the AuthList
3789                                    alt_auth_list.full[i] = true;
3790                                    // going to load all the other tokens
3791                                    load_all = true;
3792                                    // and use the "all" expiration as the token permission expirations
3793                                    load_all_exp[i] = exp;
3794                                    // add this address to all the other token permissions
3795                                    alt_load_tok_perm.add[i] = true;
3796                                    alt_load_tok_perm.has_update = true;
3797                                }
3798                                // remove "all" permission
3799                                alt_all_perm.remove[i] = true;
3800                                alt_all_perm.has_update = true;
3801                            }
3802                        }
3803                    }
3804                    if is_approve {
3805                        // add permission for this token
3806                        alt_tok_perm.add[i] = true;
3807                        // add this token to the authList
3808                        alt_auth_list.add[i] = true;
3809                    } else {
3810                        // revoke permission for this token
3811                        alt_tok_perm.remove[i] = true;
3812                        // remove this token from the AuthList
3813                        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                        // add "all" permission
3821                        alt_all_perm.add[i] = true;
3822                    } else {
3823                        // remove "all" permission
3824                        alt_all_perm.remove[i] = true;
3825                    }
3826                    alt_all_perm.has_update = true;
3827                    // clear the AuthList
3828                    alt_auth_list.clear[i] = true;
3829                    alt_auth_list.has_update = true;
3830                    // if a token was specified
3831                    if proc_info.token_given {
3832                        // also remove that token permission
3833                        alt_tok_perm.remove[i] = true;
3834                        alt_tok_perm.has_update = true;
3835                    }
3836                    // remove all other token permissions
3837                    alt_load_tok_perm.remove[i] = true;
3838                    alt_load_tok_perm.has_update = true;
3839                    // if not already going to load every owned token
3840                    if !load_all {
3841                        // load the AuthList tokens for this permission type
3842                        add_load_list.push(i);
3843                    }
3844                }
3845            }
3846        }
3847    }
3848    // update "all" permissions
3849    if alt_all_perm.has_update {
3850        // load "all" permissions if we haven't already
3851        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 there was an update to the "all" permissions
3856        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 deleted last permitted address
3865            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    // update input token permissions.
3873    // Shouldn't need to check if token was given because if it wasn't we would have thrown an
3874    // error before setting the has_update flag, but let's include the check anyway
3875    if alt_tok_perm.has_update && proc_info.token_given {
3876        // if there was an update to the token permissions
3877        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    // update the owner's AuthLists
3893    if alt_auth_list.has_update {
3894        // get the AuthLists for this address
3895        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                // shouldn't ever find it but not successfully get it, so this should never happen
3906                } else {
3907                    (&mut new_auth, false, 0usize)
3908                }
3909            // didn't find the address in the permission list
3910            } else {
3911                (&mut new_auth, false, 0usize)
3912            };
3913        let load_list: HashSet<u32> = if alt_load_tok_perm.has_update {
3914            // if we need to load other tokens create the load list
3915            // if we are loading all the owner's other tokens
3916            let list = if load_all {
3917                let inv = Inventory::new(storage, owner.clone())?;
3918                let mut set = inv.to_set(storage)?;
3919                // above, we already processed the input token, so remove it from the load list
3920                set.remove(&proc_info.idx);
3921                set
3922            // just loading the tokens in the appropriate AuthList
3923            } 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                // don't load the input token if given
3929                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                    // shouldn't ever fail this ownership check, but let's be safe
3940                    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        // do for each PermissionType
3959        for i in 0..num_perm_types {
3960            // if revoked all individual token permissions
3961            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 gave permission to all individual tokens (except the input token)
3967            } else if alt_auth_list.full[i] {
3968                auth.tokens[i] = load_list.iter().copied().collect();
3969                // if this was an ApproveToken done to an address with ALL permission
3970                // also add the specified token
3971                if alt_auth_list.add[i] {
3972                    auth.tokens[i].push(proc_info.idx);
3973                }
3974                updated = true;
3975            // else if just adding the input token (shouldn't need the token_given check)
3976            } 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 just revoking perm on the input token (don't need the token_given check)
3982            } 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 a change was made
3990        if updated {
3991            let mut auth_store = PrefixedStorage::new(PREFIX_AUTHLIST, storage);
3992            let mut save_it = true;
3993            // if the address has no authorized tokens
3994            if auth.tokens.iter().all(|t| t.is_empty()) {
3995                // and it was a pre-existing AuthList
3996                if found {
3997                    // if it was the only authorized address,
3998                    // remove the storage entry
3999                    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                // address had no previous authorization so no need to add it
4006                } else {
4007                    save_it = false;
4008                }
4009            // AuthList has data, so save it
4010            } else {
4011                // if it is a new address, add it to the list
4012                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
4024/// Returns bool
4025///
4026/// adds or removes permissions for an address based on the alterations table
4027///
4028/// # Arguments
4029///
4030/// * `perms` - a mutable reference to the list of permissions
4031/// * `alter_table` - a reference to the AlterPermTable to drive the Permission changes
4032/// * `address` - a reference to the address being added/revoked permission
4033/// * `expiration` - slice of Expirations for each PermissionType
4034/// * `num_perm_types` - the number of permission types
4035fn 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        // shouldn't ever find it but not successfully get it, so this should never happen
4051        } else {
4052            (&mut new_perm, false, 0usize)
4053        }
4054    // didn't find the address in the permission list
4055    } else {
4056        (&mut new_perm, false, 0usize)
4057    };
4058    // do for each PermissionType
4059    #[allow(clippy::needless_range_loop)]
4060    for i in 0..num_perm_types {
4061        // if supposed to add permission
4062        if alter_table.add[i] {
4063            // if it already has permission for this type
4064            if let Some(old_exp) = perm.expirations[i] {
4065                // if the new expiration is different
4066                if old_exp != expiration[i] {
4067                    perm.expirations[i] = Some(expiration[i]);
4068                    updated = true;
4069                }
4070            // new permission
4071            } else {
4072                perm.expirations[i] = Some(expiration[i]);
4073                updated = true;
4074            }
4075        // otherwise if we are supposed to remove permission
4076        } else if alter_table.remove[i] {
4077            // if it has permission for this type
4078            if perm.expirations[i].is_some() {
4079                // remove it
4080                perm.expirations[i] = None;
4081                updated = true;
4082            }
4083        }
4084    }
4085    // if a change was made
4086    if updated {
4087        // if this address had no permissions to start
4088        if !found {
4089            perms.push(new_perm);
4090        // if the last permission got revoked
4091        } else if perm.expirations.iter().all(|&e| e.is_none()) {
4092            perms.swap_remove(pos);
4093        }
4094    }
4095    updated
4096}
4097
4098// a receiver, their code hash, and whether they implement BatchReceiveNft
4099pub struct CacheReceiverInfo {
4100    // the contract address
4101    pub contract: CanonicalAddr,
4102    // the contract's registration info
4103    pub registration: ReceiveRegistration,
4104}
4105
4106/// Returns a StdResult<Vec<CosmosMsg>> list of ReceiveNft and BatchReceiveNft callacks that
4107/// should be done resulting from one Send
4108///
4109/// # Arguments
4110///
4111/// * `storage` - a reference to this contract's storage
4112/// * `contract_human` - a reference to the human address of the contract receiving the tokens
4113/// * `contract` - a reference to the canonical address of the contract receiving the tokens
4114/// * `receiver_info` - optional code hash and BatchReceiveNft implementation status of recipient contract
4115/// * `send_from_list` - list of SendFroms containing all the owners and their tokens being sent
4116/// * `msg` - a reference to the optional msg used to control ReceiveNft logic
4117/// * `sender` - a reference to the address that is sending the tokens
4118/// * `receivers` - a mutable reference the list of receiver contracts and their registration
4119///                 info
4120#[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 BatchReceiveNft is implemented, use it
4161        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        //otherwise do a bunch of BatchReceiveNft
4171        } 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
4186// an owner's inventory and the tokens they lost in this tx
4187pub struct InventoryUpdate {
4188    // owner's inventory
4189    pub inventory: Inventory,
4190    // the list of lost tokens
4191    pub remove: HashSet<u32>,
4192}
4193
4194/// Returns StdResult<()>
4195///
4196/// update owners' inventories and AuthLists to reflect recent burns/transfers
4197///
4198/// # Arguments
4199///
4200/// * `storage` - a mutable reference to the contract's storage
4201/// * `updates` - a slice of an InventoryUpdate list to modify and store new inventories/AuthLists
4202/// * `num_perm_types` - the number of permission types
4203fn 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 the inventories
4211        update.inventory.save(storage)?;
4212        // update the AuthLists if tokens were lost
4213        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/// Returns StdResult<CanonicalAddr>
4238///
4239/// transfers a token, clears the token's permissions, and returns the previous owner's address
4240///
4241/// # Arguments
4242///
4243/// * `deps` - a mutable reference to Extern containing all the contract's external dependencies
4244/// * `block` - a reference to the current BlockInfo
4245/// * `config` - a mutable reference to the Config
4246/// * `sender` - a reference to the message sender address
4247/// * `token_id` - token id String of token being transferred
4248/// * `recipient` - the recipient's address
4249/// * `oper_for` - a mutable reference to a list of owners that gave the sender "all" permission
4250/// * `inv_updates` - a mutable reference to the list of token inventories to update
4251/// * `memo` - optional memo for the transfer tx
4252#[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    // throw error if ownership would not change
4281    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    // save updated token info
4292    let mut info_store = PrefixedStorage::new(PREFIX_INFOS, &mut deps.storage);
4293    json_save(&mut info_store, &idx.to_le_bytes(), &token)?;
4294    // log the inventory changes
4295    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 updating the recipient's inventory
4311        if inv_upd.inventory.owner == recipient {
4312            inv_upd.inventory.insert(&mut deps.storage, idx, false)?;
4313        // else updating the old owner's inventory
4314        } 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 the tx
4326    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
4339// list of tokens sent from one previous owner
4340pub struct SendFrom {
4341    // the owner's address
4342    pub owner: HumanAddr,
4343    // the tokens that were sent
4344    pub token_ids: Vec<String>,
4345}
4346
4347/// Returns StdResult<Vec<CosmosMsg>>
4348///
4349/// transfer or sends a list of tokens and returns a list of ReceiveNft callbacks if applicable
4350///
4351/// # Arguments
4352///
4353/// * `deps` - a mutable reference to Extern containing all the contract's external dependencies
4354/// * `env` - a reference to the Env of the contract's environment
4355/// * `config` - a mutable reference to the Config
4356/// * `sender` - a reference to the message sender address
4357/// * `transfers` - optional list of transfers to perform
4358/// * `sends` - optional list of sends to perform
4359fn 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                // compile list of all tokens being sent from each owner in this Send
4406                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            // get BatchReceiveNft and ReceiveNft msgs for all the tokens sent in this Send
4418            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
4435/// Returns StdResult<()>
4436///
4437/// burns a list of tokens
4438///
4439/// # Arguments
4440///
4441/// * `deps` - a mutable reference to Extern containing all the contract's external dependencies
4442/// * `block` - a reference to the current BlockInfo
4443/// * `config` - a mutable reference to the Config
4444/// * `sender` - a reference to the message sender address
4445/// * `burns` - list of burns to perform
4446fn 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            // log the inventory change
4473            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            // decrement token count
4493            config.token_cnt = config.token_cnt.saturating_sub(1);
4494            // remove from maps
4495            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            // remove the token info
4500            let mut info_store = PrefixedStorage::new(PREFIX_INFOS, &mut deps.storage);
4501            remove(&mut info_store, &token_key);
4502            // remove metadata if existent
4503            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            // remove mint run info if existent
4508            let mut run_store = PrefixedStorage::new(PREFIX_MINT_RUN, &mut deps.storage);
4509            remove(&mut run_store, &token_key);
4510            // remove royalty info if existent
4511            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 the tx
4520            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
4536/// Returns <Vec<String>>
4537///
4538/// mints a list of new tokens and returns the ids of the tokens minted
4539///
4540/// # Arguments
4541///
4542/// * `deps` - a mutable reference to Extern containing all the contract's external dependencies
4543/// * `env` - a reference to the Env of the contract's environment
4544/// * `config` - a mutable reference to the Config
4545/// * `sender_raw` - a reference to the message sender address
4546/// * `mints` - list of mints to perform
4547fn 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        // check if id already exists
4560        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        // increment token count
4569        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        // map new token id to its index
4573        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        // save new token info
4588        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        // add token to owner's list
4592        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        // map index to id
4604        let mut map2id = PrefixedStorage::new(PREFIX_MAP_TO_ID, &mut deps.storage);
4605        save(&mut map2id, &token_key, &id)?;
4606
4607        //
4608        // If you wanted to store an additional data struct for each NFT, you would create
4609        // a new prefix and store with the `token_key` like below
4610        //
4611        // save the metadata
4612        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        // save the mint run info
4623        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        // check/save royalty information only if the token is transferable
4643        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        //
4654        //
4655
4656        // store the tx
4657        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        // increment index for next mint
4668        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    // save all the updated inventories
4673    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
4680/// Returns StdResult<()>
4681///
4682/// verifies the royalty information is valid and if so, stores the royalty info for the token
4683/// or as default
4684///
4685/// # Arguments
4686///
4687/// * `storage` - a mutable reference to the storage for this RoyaltyInfo
4688/// * `api` - a reference to the Api used to convert human and canonical addresses
4689/// * `royalty_info` - an optional reference to the RoyaltyInfo to store
4690/// * `default` - an optional reference to the default StoredRoyaltyInfo to use if royalty_info is
4691///               not provided
4692/// * `key` - the storage key (either token key or default key)
4693fn 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 RoyaltyInfo is provided, check and save it
4701    if let Some(royal_inf) = royalty_info {
4702        // the allowed message length won't let enough u16 rates to overflow u128
4703        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
4726/// Returns StdResult<()>
4727///
4728/// makes sure that Metadata does not have both `token_uri` and `extension`
4729///
4730/// # Arguments
4731///
4732/// * `metadata` - a reference to Metadata
4733fn 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
4742/// Returns StdResult<Option<CanonicalAddr>> from determining the querying address (if possible) either
4743/// from a permit validation or a ViewerInfo
4744///
4745/// # Arguments
4746///
4747/// * `deps` - a reference to Extern containing all the contract's external dependencies
4748/// * `viewer` - optional address and key making an authenticated query request
4749/// * `from_permit` - the address derived from an Owner permit, if applicable
4750fn 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
4768// used to cache owner information for dossier_list()
4769pub struct OwnerInfo {
4770    // the owner's address
4771    pub owner: CanonicalAddr,
4772    // the view_owner privacy override
4773    pub owner_is_public: bool,
4774    // inventory approvals
4775    pub inventory_approvals: Vec<Snip721Approval>,
4776    // expiration for global view_owner approval if applicable
4777    pub view_owner_exp: Option<Expiration>,
4778    // expiration for global view_private_metadata approval if applicable
4779    pub view_meta_exp: Option<Expiration>,
4780}
4781
4782/// Returns StdResult<Vec<BatchNftDossierElement>> of all the token information the querier is permitted to
4783/// view for multiple tokens.  This may include the owner, the public metadata, the private metadata, royalty
4784/// information, mint run information, whether the token is unwrapped, whether the token is
4785/// transferable, and the token and inventory approvals
4786///
4787/// # Arguments
4788///
4789/// * `deps` - a reference to Extern containing all the contract's external dependencies
4790/// * `token_ids` - list of token ids to retrieve the info of
4791/// * `viewer` - optional address and key making an authenticated query request
4792/// * `include_expired` - optionally true if the Approval lists should include expired Approvals
4793/// * `from_permit` - address derived from an Owner permit, if applicable
4794pub 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    // TODO remove this when BlockInfo becomes available to queries
4810    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    // used to shortcut permission checks if the viewer is already a known operator for a list of owners
4822    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    // set up all the immutable storage references
4828    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        // if token supply is private, don't leak that the token id does not exist
4841        // instead just say they are not authorized for that token
4842        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        // get the owner info either from the cache or storage
4850        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        // get the owner if permitted
4872        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        // get the public metadata
4891        let token_key = idx.to_le_bytes();
4892        let public_metadata: Option<Metadata> = may_load(&pub_store, &token_key)?;
4893        // get the private metadata if it is not sealed and if the viewer is permitted
4894        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        // get the royalty information if present
4920        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        // get the mint run information
4939        let mint_run: StoredMintRunInfo = load(&run_store, &token_key)?;
4940        // get the token approvals
4941        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        // determine if ownership is public
4949        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        // determine if private metadata is public
4960        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        // if the viewer is the owner, display the approvals
4970        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}