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}