use serde::de::DeserializeOwned;
use serde::Serialize;
use cosmwasm_std::{to_binary, Addr, Binary, BlockInfo, Deps, Env, Order, StdError, StdResult};
use cw721::{CustomMsg, Expiration};
use cw_storage_plus::Bound;
use cw_utils::maybe_addr;
use crate::msg::{
AllModelsResponse, AllNftInfoResponse, AllNftsResponse, ApprovalResponse, ApprovalsResponse,
CollectionInfoResponse, ContractInfoResponse, MinterResponse, ModelInfoResponse,
ModelsResponse, NftInfoResponse, NumModelsResponse, NumTokensResponse, OperatorsResponse,
OwnerOfResponse, QueryMsg, RoyaltyInfoResponse, TokensResponse,
};
use crate::state::COLLECTION_INFO;
use crate::state::{AnoneCw721Contract, Approval, ModelInfo, TokenInfo};
const DEFAULT_LIMIT: u32 = 10;
const MAX_LIMIT: u32 = 30;
impl<'a, T, C> AnoneCw721Contract<'a, T, C>
where
T: Serialize + DeserializeOwned + Clone,
C: CustomMsg,
{
pub fn minter(&self, deps: Deps) -> StdResult<MinterResponse> {
let minter_addr = self.minter.load(deps.storage)?;
Ok(MinterResponse {
minter: minter_addr.to_string(),
})
}
fn contract_info(&self, deps: Deps) -> StdResult<ContractInfoResponse> {
self.contract_info.load(deps.storage)
}
fn num_tokens(&self, deps: Deps) -> StdResult<NumTokensResponse> {
let count = self.token_count(deps.storage)?;
Ok(NumTokensResponse { count })
}
fn num_models(&self, deps: Deps) -> StdResult<NumModelsResponse> {
let count = self.model_count(deps.storage)?;
Ok(NumModelsResponse { count })
}
fn owner_of(
&self,
deps: Deps,
env: Env,
token_id: String,
include_expired: bool,
) -> StdResult<OwnerOfResponse> {
let info = self.tokens.load(deps.storage, &token_id)?;
Ok(OwnerOfResponse {
owner: info.owner.to_string(),
approvals: humanize_approvals(&env.block, &info, include_expired),
})
}
fn nft_info(&self, deps: Deps, token_id: String) -> StdResult<NftInfoResponse<T>> {
let info = self.tokens.load(deps.storage, &token_id)?;
Ok(NftInfoResponse {
model_id: info.model_id,
token_uri: info.token_uri,
size: info.size,
extension: info.extension,
})
}
fn all_nft_info(
&self,
deps: Deps,
env: Env,
token_id: String,
include_expired: bool,
) -> StdResult<AllNftInfoResponse<T>> {
let info = self.tokens.load(deps.storage, &token_id)?;
Ok(AllNftInfoResponse {
access: OwnerOfResponse {
owner: info.owner.to_string(),
approvals: humanize_approvals(&env.block, &info, include_expired),
},
info: NftInfoResponse {
model_id: info.model_id,
token_uri: info.token_uri,
size: info.size,
extension: info.extension,
},
})
}
fn model_info(&self, deps: Deps, model_id: String) -> StdResult<ModelInfoResponse<T>> {
let info = self.models.load(deps.storage, &model_id)?;
Ok(ModelInfoResponse {
owner: info.owner.to_string(),
model_uri: info.model_uri,
extension: info.extension,
})
}
fn operators(
&self,
deps: Deps,
env: Env,
owner: String,
include_expired: bool,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<OperatorsResponse> {
let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
let start_addr = maybe_addr(deps.api, start_after)?;
let start = start_addr.map(|addr| Bound::exclusive(addr.as_ref()));
let owner_addr = deps.api.addr_validate(&owner)?;
let res: StdResult<Vec<_>> = self
.operators
.prefix(&owner_addr)
.range(deps.storage, start, None, Order::Ascending)
.filter(|r| {
include_expired || r.is_err() || !r.as_ref().unwrap().1.is_expired(&env.block)
})
.take(limit)
.map(parse_approval)
.collect();
Ok(OperatorsResponse { operators: res? })
}
fn approval(
&self,
deps: Deps,
env: Env,
token_id: String,
spender: String,
include_expired: bool,
) -> StdResult<ApprovalResponse> {
let token = self.tokens.load(deps.storage, &token_id)?;
if token.owner == spender {
let approval = Approval {
spender: token.owner,
expires: Expiration::Never {},
};
return Ok(ApprovalResponse { approval });
}
let filtered: Vec<_> = token
.approvals
.into_iter()
.filter(|t| t.spender == spender)
.filter(|t| include_expired || !t.is_expired(&env.block))
.map(|a| Approval {
spender: a.spender,
expires: a.expires,
})
.collect();
if filtered.is_empty() {
return Err(StdError::not_found("Approval not found"));
}
let approval = filtered[0].clone();
Ok(ApprovalResponse { approval })
}
fn approvals(
&self,
deps: Deps,
env: Env,
token_id: String,
include_expired: bool,
) -> StdResult<ApprovalsResponse> {
let token = self.tokens.load(deps.storage, &token_id)?;
let approvals: Vec<_> = token
.approvals
.into_iter()
.filter(|t| include_expired || !t.is_expired(&env.block))
.map(|a| Approval {
spender: a.spender,
expires: a.expires,
})
.collect();
Ok(ApprovalsResponse { approvals })
}
fn tokens(
&self,
deps: Deps,
owner: String,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<TokensResponse> {
let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
let start = start_after.map(Bound::exclusive);
let owner_addr = deps.api.addr_validate(&owner)?;
let tokens: Vec<String> = self
.tokens
.idx
.owner
.prefix(owner_addr)
.keys(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|x| x.map(|addr| addr.to_string()))
.collect::<StdResult<Vec<_>>>()?;
Ok(TokensResponse { tokens })
}
fn all_tokens(
&self,
deps: Deps,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<TokensResponse> {
let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
let start = start_after.map(Bound::exclusive);
let tokens: StdResult<Vec<String>> = self
.tokens
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|item| item.map(|(k, _)| k))
.collect();
Ok(TokensResponse { tokens: tokens? })
}
fn all_tokens_info(
&self,
deps: Deps,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<AllNftsResponse<T>> {
let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
let start = start_after.map(Bound::exclusive);
let tokens: StdResult<Vec<TokenInfo<T>>> = self
.tokens
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|item| item.map(|(_, info)| info))
.collect();
Ok(AllNftsResponse {
all_tokens_info: tokens?,
})
}
fn all_models(
&self,
deps: Deps,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<ModelsResponse> {
let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
let start = start_after.map(Bound::exclusive);
let models: StdResult<Vec<String>> = self
.models
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|item| item.map(|(k, _)| k))
.collect();
Ok(ModelsResponse { models: models? })
}
fn all_models_info(
&self,
deps: Deps,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<AllModelsResponse<T>> {
let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
let start = start_after.map(Bound::exclusive);
let models: StdResult<Vec<ModelInfo<T>>> = self
.models
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|item| item.map(|(_, info)| info))
.collect();
Ok(AllModelsResponse {
all_models_info: models?,
})
}
pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::Minter {} => to_binary(&self.minter(deps)?),
QueryMsg::ContractInfo {} => to_binary(&self.contract_info(deps)?),
QueryMsg::NftInfo { token_id } => to_binary(&self.nft_info(deps, token_id)?),
QueryMsg::OwnerOf {
token_id,
include_expired,
} => {
to_binary(&self.owner_of(deps, env, token_id, include_expired.unwrap_or(false))?)
}
QueryMsg::AllNftInfo {
token_id,
include_expired,
} => to_binary(&self.all_nft_info(
deps,
env,
token_id,
include_expired.unwrap_or(false),
)?),
QueryMsg::ModelInfo { model_id } => to_binary(&self.model_info(deps, model_id)?),
QueryMsg::AllOperators {
owner,
include_expired,
start_after,
limit,
} => to_binary(&self.operators(
deps,
env,
owner,
include_expired.unwrap_or(false),
start_after,
limit,
)?),
QueryMsg::NumTokens {} => to_binary(&self.num_tokens(deps)?),
QueryMsg::NumModels {} => to_binary(&self.num_models(deps)?),
QueryMsg::Tokens {
owner,
start_after,
limit,
} => to_binary(&self.tokens(deps, owner, start_after, limit)?),
QueryMsg::AllTokens { start_after, limit } => {
to_binary(&self.all_tokens(deps, start_after, limit)?)
}
QueryMsg::AllTokensInfo { start_after, limit } => {
to_binary(&self.all_tokens_info(deps, start_after, limit)?)
}
QueryMsg::AllModels { start_after, limit } => {
to_binary(&self.all_models(deps, start_after, limit)?)
}
QueryMsg::AllModelsInfo { start_after, limit } => {
to_binary(&self.all_models_info(deps, start_after, limit)?)
}
QueryMsg::Approval {
token_id,
spender,
include_expired,
} => to_binary(&self.approval(
deps,
env,
token_id,
spender,
include_expired.unwrap_or(false),
)?),
QueryMsg::Approvals {
token_id,
include_expired,
} => {
to_binary(&self.approvals(deps, env, token_id, include_expired.unwrap_or(false))?)
}
QueryMsg::CollectionInfo {} => to_binary(&query_config(deps)?),
}
}
}
fn query_config(deps: Deps) -> StdResult<CollectionInfoResponse> {
let info = COLLECTION_INFO.load(deps.storage)?;
let royalty_info_res: Option<RoyaltyInfoResponse> = match info.royalty_info {
Some(royalty_info) => Some(RoyaltyInfoResponse {
payment_address: royalty_info.payment_address.to_string(),
share: royalty_info.share,
}),
None => None,
};
Ok(CollectionInfoResponse {
creator: info.creator,
description: info.description,
image: info.image,
external_link: info.external_link,
royalty_info: royalty_info_res,
})
}
fn parse_approval(item: StdResult<(Addr, Expiration)>) -> StdResult<Approval> {
item.map(|(spender, expires)| Approval {
spender: spender,
expires,
})
}
fn humanize_approvals<T>(
block: &BlockInfo,
info: &TokenInfo<T>,
include_expired: bool,
) -> Vec<Approval> {
info.approvals
.iter()
.filter(|apr| include_expired || !apr.is_expired(block))
.map(humanize_approval)
.collect()
}
fn humanize_approval(approval: &Approval) -> Approval {
Approval {
spender: approval.spender.clone(),
expires: approval.expires,
}
}