sg_marketplace_common/
nft.rs1use cosmwasm_std::{
2    to_json_binary, Addr, Api, BlockInfo, Empty, MessageInfo, QuerierWrapper, StdError, StdResult,
3    WasmMsg,
4};
5use cw721::{ApprovalResponse, Cw721ExecuteMsg, OwnerOfResponse};
6use cw721_base::helpers::Cw721Contract;
7use sg721::RoyaltyInfo;
8use sg721_base::msg::{CollectionInfoResponse, QueryMsg as Sg721QueryMsg};
9use sg_std::{Response, SubMsg};
10use std::marker::PhantomData;
11
12pub use crate::errors::MarketplaceStdError;
13
14pub fn transfer_nft(
16    collection: &Addr,
17    token_id: &str,
18    recipient: &Addr,
19    response: Response,
20) -> Response {
21    response.add_submessage(SubMsg::new(WasmMsg::Execute {
22        contract_addr: collection.to_string(),
23        msg: to_json_binary(&Cw721ExecuteMsg::TransferNft {
24            token_id: token_id.to_string(),
25            recipient: recipient.to_string(),
26        })
27        .unwrap(),
28        funds: vec![],
29    }))
30}
31
32pub fn owner_of(
34    querier: &QuerierWrapper,
35    collection: &Addr,
36    token_id: &str,
37) -> StdResult<OwnerOfResponse> {
38    Cw721Contract::<Empty, Empty>(collection.clone(), PhantomData, PhantomData)
39        .owner_of(querier, token_id, false)
40}
41
42pub fn only_owner(
44    querier: &QuerierWrapper,
45    info: &MessageInfo,
46    collection: &Addr,
47    token_id: &str,
48) -> StdResult<()> {
49    let owner_of_response = owner_of(querier, collection, token_id)?;
50    if owner_of_response.owner != info.sender {
51        return Err(StdError::generic_err("Unauthorized"));
52    }
53    Ok(())
54}
55
56pub fn has_approval(
58    querier: &QuerierWrapper,
59    spender: &Addr,
60    collection: &Addr,
61    token_id: &str,
62    include_expired: Option<bool>,
63) -> StdResult<ApprovalResponse> {
64    Cw721Contract::<Empty, Empty>(collection.clone(), PhantomData, PhantomData).approval(
65        querier,
66        token_id,
67        spender.as_str(),
68        include_expired,
69    )
70}
71
72pub fn only_with_owner_approval(
74    querier: &QuerierWrapper,
75    info: &MessageInfo,
76    collection: &Addr,
77    token_id: &str,
78    contract: &Addr,
79) -> Result<(), MarketplaceStdError> {
80    let owner_of_response = owner_of(querier, collection, token_id)?;
81    if owner_of_response.owner != info.sender {
82        return Err(MarketplaceStdError::Unauthorized(
83            "sender is not owner".to_string(),
84        ));
85    }
86    if !owner_of_response
87        .approvals
88        .iter()
89        .any(|a| a.spender == *contract)
90    {
91        return Err(MarketplaceStdError::Unauthorized(
92            "contract is not approved".to_string(),
93        ));
94    }
95
96    Ok(())
97}
98
99pub fn only_tradable(
101    querier: &QuerierWrapper,
102    block: &BlockInfo,
103    collection: &Addr,
104) -> Result<(), MarketplaceStdError> {
105    let response: Result<CollectionInfoResponse, StdError> =
106        querier.query_wasm_smart(collection.clone(), &Sg721QueryMsg::CollectionInfo {});
107
108    match response {
109        Ok(collection_info) => match collection_info.start_trading_time {
110            Some(start_trading_time) => {
111                if start_trading_time > block.time {
112                    Err(MarketplaceStdError::CollectionNotTradable {})
113                } else {
114                    Ok(())
115                }
116            }
117            None => Ok(()),
119        },
120        Err(_) => Ok(()),
122    }
123}
124
125pub fn load_collection_royalties(
127    querier: &QuerierWrapper,
128    api: &dyn Api,
129    collection_addr: &Addr,
130) -> StdResult<Option<RoyaltyInfo>> {
131    let collection_info: CollectionInfoResponse =
132        querier.query_wasm_smart(collection_addr, &Sg721QueryMsg::CollectionInfo {})?;
133
134    let royalty_info: Option<RoyaltyInfo> = match collection_info.royalty_info {
135        Some(royalty_info_response) => Some(RoyaltyInfo {
136            share: royalty_info_response.share,
137            payment_address: api.addr_validate(&royalty_info_response.payment_address)?,
138        }),
139        None => None,
140    };
141
142    Ok(royalty_info)
143}