From 2c684b8051f0f26401d1c5993216b709e47c91d3 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 27 Feb 2024 12:23:53 +0100 Subject: [PATCH 01/47] rename states: tokens -> nft_info, contract_info -> collection_info --- contracts/cw2981-royalties/src/query.rs | 2 +- contracts/cw721-base/src/execute.rs | 16 ++++++++-------- contracts/cw721-base/src/query.rs | 16 ++++++++-------- contracts/cw721-base/src/state.rs | 16 ++++++++-------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/contracts/cw2981-royalties/src/query.rs b/contracts/cw2981-royalties/src/query.rs index 53c20589f..6028b3b2d 100644 --- a/contracts/cw2981-royalties/src/query.rs +++ b/contracts/cw2981-royalties/src/query.rs @@ -10,7 +10,7 @@ pub fn query_royalties_info( sale_price: Uint128, ) -> StdResult { let contract = Cw2981Contract::default(); - let token_info = contract.tokens.load(deps.storage, &token_id)?; + let token_info = contract.nft_info.load(deps.storage, &token_id)?; let royalty_percentage = match token_info.extension { Some(ref ext) => match ext.royalty_percentage { diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 246919bc4..07c09c766 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -30,7 +30,7 @@ where name: msg.name, symbol: msg.symbol, }; - self.contract_info.save(deps.storage, &contract_info)?; + self.collection_info.save(deps.storage, &contract_info)?; let owner = match msg.minter { Some(owner) => deps.api.addr_validate(&owner)?, @@ -120,7 +120,7 @@ where token_uri, extension, }; - self.tokens + self.nft_info .update(deps.storage, &token_id, |old| match old { Some(_) => Err(ContractError::Claimed {}), None => Ok(token), @@ -337,10 +337,10 @@ where info: MessageInfo, token_id: String, ) -> Result, ContractError> { - let token = self.tokens.load(deps.storage, &token_id)?; + let token = self.nft_info.load(deps.storage, &token_id)?; self.check_can_send(deps.as_ref(), &env, &info, &token)?; - self.tokens.remove(deps.storage, &token_id)?; + self.nft_info.remove(deps.storage, &token_id)?; self.decrement_tokens(deps.storage)?; Ok(Response::new() @@ -366,13 +366,13 @@ where recipient: &str, token_id: &str, ) -> Result, ContractError> { - let mut token = self.tokens.load(deps.storage, token_id)?; + let mut token = self.nft_info.load(deps.storage, token_id)?; // ensure we have permissions self.check_can_send(deps.as_ref(), env, info, &token)?; // set owner and remove existing approvals token.owner = deps.api.addr_validate(recipient)?; token.approvals = vec![]; - self.tokens.save(deps.storage, token_id, &token)?; + self.nft_info.save(deps.storage, token_id, &token)?; Ok(token) } @@ -388,7 +388,7 @@ where add: bool, expires: Option, ) -> Result, ContractError> { - let mut token = self.tokens.load(deps.storage, token_id)?; + let mut token = self.nft_info.load(deps.storage, token_id)?; // ensure we have permissions self.check_can_approve(deps.as_ref(), env, info, &token)?; @@ -410,7 +410,7 @@ where token.approvals.push(approval); } - self.tokens.save(deps.storage, token_id, &token)?; + self.nft_info.save(deps.storage, token_id, &token)?; Ok(token) } diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index 52db6a7ca..a5b813b6f 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -27,7 +27,7 @@ where Q: CustomMsg, { fn contract_info(&self, deps: Deps) -> StdResult { - self.contract_info.load(deps.storage) + self.collection_info.load(deps.storage) } fn num_tokens(&self, deps: Deps) -> StdResult { @@ -36,7 +36,7 @@ where } fn nft_info(&self, deps: Deps, token_id: String) -> StdResult> { - let info = self.tokens.load(deps.storage, &token_id)?; + let info = self.nft_info.load(deps.storage, &token_id)?; Ok(NftInfoResponse { token_uri: info.token_uri, extension: info.extension, @@ -50,7 +50,7 @@ where token_id: String, include_expired: bool, ) -> StdResult { - let info = self.tokens.load(deps.storage, &token_id)?; + let info = self.nft_info.load(deps.storage, &token_id)?; Ok(OwnerOfResponse { owner: info.owner.to_string(), approvals: humanize_approvals(&env.block, &info, include_expired), @@ -125,7 +125,7 @@ where spender: String, include_expired: bool, ) -> StdResult { - let token = self.tokens.load(deps.storage, &token_id)?; + let token = self.nft_info.load(deps.storage, &token_id)?; // token owner has absolute approval if token.owner == spender { @@ -164,7 +164,7 @@ where token_id: String, include_expired: bool, ) -> StdResult { - let token = self.tokens.load(deps.storage, &token_id)?; + let token = self.nft_info.load(deps.storage, &token_id)?; let approvals: Vec<_> = token .approvals .into_iter() @@ -190,7 +190,7 @@ where let owner_addr = deps.api.addr_validate(&owner)?; let tokens: Vec = self - .tokens + .nft_info .idx .owner .prefix(owner_addr) @@ -211,7 +211,7 @@ where let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); let tokens: StdResult> = self - .tokens + .nft_info .range(deps.storage, start, None, Order::Ascending) .take(limit) .map(|item| item.map(|(k, _)| k)) @@ -227,7 +227,7 @@ where token_id: String, include_expired: bool, ) -> StdResult> { - let info = self.tokens.load(deps.storage, &token_id)?; + let info = self.nft_info.load(deps.storage, &token_id)?; Ok(AllNftInfoResponse { access: OwnerOfResponse { owner: info.owner.to_string(), diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 48bca6bf5..6c5a029cc 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -14,11 +14,11 @@ where Q: CustomMsg, E: CustomMsg, { - pub contract_info: Item<'a, ContractInfoResponse>, + pub collection_info: Item<'a, ContractInfoResponse>, pub token_count: Item<'a, u64>, /// Stored as (granter, operator) giving operator full control over granter's account pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, - pub tokens: IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a, T>>, + pub nft_info: IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a, T>>, pub withdraw_address: Item<'a, String>, pub(crate) _custom_response: PhantomData, @@ -61,21 +61,21 @@ where Q: CustomMsg, { fn new( - contract_key: &'a str, + collection_info_key: &'a str, token_count_key: &'a str, operator_key: &'a str, - tokens_key: &'a str, - tokens_owner_key: &'a str, + nft_info_key: &'a str, + nft_info_owner_key: &'a str, withdraw_address_key: &'a str, ) -> Self { let indexes = TokenIndexes { - owner: MultiIndex::new(token_owner_idx, tokens_key, tokens_owner_key), + owner: MultiIndex::new(token_owner_idx, nft_info_key, nft_info_owner_key), }; Self { - contract_info: Item::new(contract_key), + collection_info: Item::new(collection_info_key), token_count: Item::new(token_count_key), operators: Map::new(operator_key), - tokens: IndexedMap::new(tokens_key, indexes), + nft_info: IndexedMap::new(nft_info_key, indexes), withdraw_address: Item::new(withdraw_address_key), _custom_response: PhantomData, _custom_execute: PhantomData, From c3dc54cc34736bfa5afb4684bd9e1ca300be8823 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 27 Feb 2024 14:47:30 +0100 Subject: [PATCH 02/47] renamed structs and deprecate: TokenInfo -> NftInfo, ContractInfo -> CollectionInfo, ContractInfoResponse -> CollectionInfoResponse --- Cargo.lock | 40 +++++++++---------- Cargo.toml | 2 +- contracts/cw721-base/Cargo.toml | 1 + contracts/cw721-base/src/contract_tests.rs | 6 +-- contracts/cw721-base/src/execute.rs | 16 ++++---- contracts/cw721-base/src/helpers.rs | 13 ++++-- contracts/cw721-base/src/msg.rs | 8 +++- contracts/cw721-base/src/query.rs | 12 +++--- contracts/cw721-base/src/state.rs | 21 +++++----- .../cw721-expiration/src/contract_tests.rs | 6 +-- contracts/cw721-expiration/src/lib.rs | 5 ++- contracts/cw721-expiration/src/msg.rs | 8 +++- contracts/cw721-expiration/src/query.rs | 14 +++++-- .../cw721-non-transferable/examples/schema.rs | 8 +++- contracts/cw721-non-transferable/src/msg.rs | 6 ++- packages/cw721/examples/schema.rs | 9 +++-- packages/cw721/src/lib.rs | 7 ++-- packages/cw721/src/query.rs | 6 ++- packages/cw721/src/traits.rs | 7 ++-- 19 files changed, 120 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8216b9c15..d65787867 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -354,13 +354,13 @@ dependencies = [ [[package]] name = "cw2981-royalties" -version = "0.18.0" +version = "0.19.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw2 1.1.2", - "cw721 0.18.0", - "cw721-base 0.18.0", + "cw721 0.19.0", + "cw721-base 0.19.0", "schemars", "serde", "thiserror", @@ -381,7 +381,7 @@ dependencies = [ [[package]] name = "cw721" -version = "0.18.0" +version = "0.19.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -409,7 +409,7 @@ dependencies = [ [[package]] name = "cw721-base" -version = "0.18.0" +version = "0.19.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -418,7 +418,7 @@ dependencies = [ "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", - "cw721 0.18.0", + "cw721 0.19.0", "cw721-base 0.16.0", "schemars", "serde", @@ -427,7 +427,7 @@ dependencies = [ [[package]] name = "cw721-expiration" -version = "0.18.0" +version = "0.19.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -435,8 +435,8 @@ dependencies = [ "cw-ownable", "cw-storage-plus 1.2.0", "cw2 1.1.2", - "cw721 0.18.0", - "cw721-base 0.18.0", + "cw721 0.19.0", + "cw721-base 0.19.0", "schemars", "serde", "thiserror", @@ -444,7 +444,7 @@ dependencies = [ [[package]] name = "cw721-fixed-price" -version = "0.18.0" +version = "0.19.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -452,7 +452,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20", - "cw721-base 0.18.0", + "cw721-base 0.19.0", "prost 0.10.4", "schemars", "serde", @@ -461,40 +461,40 @@ dependencies = [ [[package]] name = "cw721-metadata-onchain" -version = "0.18.0" +version = "0.19.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw2 1.1.2", - "cw721 0.18.0", - "cw721-base 0.18.0", + "cw721 0.19.0", + "cw721-base 0.19.0", "schemars", "serde", ] [[package]] name = "cw721-non-transferable" -version = "0.18.0" +version = "0.19.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 1.2.0", "cw2 1.1.2", - "cw721 0.18.0", - "cw721-base 0.18.0", + "cw721 0.19.0", + "cw721-base 0.19.0", "schemars", "serde", ] [[package]] name = "cw721-receiver-tester" -version = "0.18.0" +version = "0.19.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", - "cw721 0.18.0", - "cw721-base 0.18.0", + "cw721 0.19.0", + "cw721-base 0.19.0", "schemars", "serde", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 60963487c..c81542191 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = ["packages/*", "contracts/*"] [workspace.package] -version = "0.18.0" +version = "0.19.0" edition = "2021" license = "Apache-2.0" repository = "https://github.com/CosmWasm/cw-nfts" diff --git a/contracts/cw721-base/Cargo.toml b/contracts/cw721-base/Cargo.toml index be71889b1..3b140ce27 100644 --- a/contracts/cw721-base/Cargo.toml +++ b/contracts/cw721-base/Cargo.toml @@ -2,6 +2,7 @@ name = "cw721-base" description = "Basic implementation cw721 NFTs" authors = [ + "mr-t ", "Ethan Frey ", "Orkun Külçe ", ] diff --git a/contracts/cw721-base/src/contract_tests.rs b/contracts/cw721-base/src/contract_tests.rs index a128a1939..551b70a75 100644 --- a/contracts/cw721-base/src/contract_tests.rs +++ b/contracts/cw721-base/src/contract_tests.rs @@ -6,7 +6,7 @@ use cosmwasm_std::{ }; use cw721::{ - Approval, ApprovalResponse, ContractInfoResponse, Cw721Query, Cw721ReceiveMsg, Expiration, + Approval, ApprovalResponse, CollectionInfoResponse, Cw721Query, Cw721ReceiveMsg, Expiration, NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, }; use cw_ownable::OwnershipError; @@ -55,10 +55,10 @@ fn proper_instantiation() { // it worked, let's query the state let res = contract.minter(deps.as_ref()).unwrap(); assert_eq!(Some(MINTER.to_string()), res.minter); - let info = contract.contract_info(deps.as_ref()).unwrap(); + let info = contract.collection_info(deps.as_ref()).unwrap(); assert_eq!( info, - ContractInfoResponse { + CollectionInfoResponse { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), } diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 07c09c766..05b1381b7 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -6,11 +6,11 @@ use cosmwasm_std::{ Addr, BankMsg, Binary, Coin, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, Storage, }; -use cw721::{ContractInfoResponse, Cw721Execute, Cw721ReceiveMsg, Expiration}; +use cw721::{CollectionInfoResponse, Cw721Execute, Cw721ReceiveMsg, Expiration}; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg}; -use crate::state::{Approval, Cw721Contract, TokenInfo}; +use crate::state::{Approval, Cw721Contract, NftInfo}; impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> where @@ -26,7 +26,7 @@ where info: MessageInfo, msg: InstantiateMsg, ) -> Result, ContractError> { - let contract_info = ContractInfoResponse { + let contract_info = CollectionInfoResponse { name: msg.name, symbol: msg.symbol, }; @@ -114,7 +114,7 @@ where cw_ownable::assert_owner(deps.storage, &info.sender)?; // create the token - let token = TokenInfo { + let token = NftInfo { owner: deps.api.addr_validate(&owner)?, approvals: vec![], token_uri, @@ -365,7 +365,7 @@ where info: &MessageInfo, recipient: &str, token_id: &str, - ) -> Result, ContractError> { + ) -> Result, ContractError> { let mut token = self.nft_info.load(deps.storage, token_id)?; // ensure we have permissions self.check_can_send(deps.as_ref(), env, info, &token)?; @@ -387,7 +387,7 @@ where // if add == false, remove. if add == true, remove then set with this expiration add: bool, expires: Option, - ) -> Result, ContractError> { + ) -> Result, ContractError> { let mut token = self.nft_info.load(deps.storage, token_id)?; // ensure we have permissions self.check_can_approve(deps.as_ref(), env, info, &token)?; @@ -421,7 +421,7 @@ where deps: Deps, env: &Env, info: &MessageInfo, - token: &TokenInfo, + token: &NftInfo, ) -> Result<(), ContractError> { // owner can approve if token.owner == info.sender { @@ -449,7 +449,7 @@ where deps: Deps, env: &Env, info: &MessageInfo, - token: &TokenInfo, + token: &NftInfo, ) -> Result<(), ContractError> { // owner can send if token.owner == info.sender { diff --git a/contracts/cw721-base/src/helpers.rs b/contracts/cw721-base/src/helpers.rs index ab28a52d6..9c3761886 100644 --- a/contracts/cw721-base/src/helpers.rs +++ b/contracts/cw721-base/src/helpers.rs @@ -5,7 +5,7 @@ use cosmwasm_std::{ to_json_binary, Addr, CosmosMsg, CustomMsg, QuerierWrapper, StdResult, WasmMsg, WasmQuery, }; use cw721::{ - AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, + AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; use serde::de::DeserializeOwned; @@ -118,9 +118,14 @@ impl Cw721Contract { Ok(res.count) } + #[deprecated(since = "0.19.0", note = "Please use collection_info instead")] + pub fn contract_info(&self, querier: &QuerierWrapper) -> StdResult { + self.collection_info(querier) + } + /// With metadata extension - pub fn contract_info(&self, querier: &QuerierWrapper) -> StdResult { - let req = QueryMsg::ContractInfo {}; + pub fn collection_info(&self, querier: &QuerierWrapper) -> StdResult { + let req = QueryMsg::CollectionInfo {}; self.query(querier, req) } @@ -179,7 +184,7 @@ impl Cw721Contract { /// returns true if the contract supports the metadata extension pub fn has_metadata(&self, querier: &QuerierWrapper) -> bool { - self.contract_info(querier).is_ok() + self.collection_info(querier).is_ok() } /// returns true if the contract supports the enumerable extension diff --git a/contracts/cw721-base/src/msg.rs b/contracts/cw721-base/src/msg.rs index 6af543714..5fe2f8757 100644 --- a/contracts/cw721-base/src/msg.rs +++ b/contracts/cw721-base/src/msg.rs @@ -125,10 +125,14 @@ pub enum QueryMsg { #[returns(cw721::NumTokensResponse)] NumTokens {}, + #[deprecated(since = "0.19.0", note = "Please use CollectionInfo instead")] + #[returns(cw721::CollectionInfoResponse)] + ContractInfo {}, + /// With MetaData Extension. /// Returns top-level metadata about the contract - #[returns(cw721::ContractInfoResponse)] - ContractInfo {}, + #[returns(cw721::CollectionInfoResponse)] + CollectionInfo {}, /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index a5b813b6f..496944d24 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -6,7 +6,7 @@ use cosmwasm_std::{ }; use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, Cw721Query, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, Cw721Query, Expiration, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; @@ -14,7 +14,7 @@ use cw_storage_plus::Bound; use cw_utils::maybe_addr; use crate::msg::{MinterResponse, QueryMsg}; -use crate::state::{Approval, Cw721Contract, TokenInfo}; +use crate::state::{Approval, Cw721Contract, NftInfo}; const DEFAULT_LIMIT: u32 = 10; const MAX_LIMIT: u32 = 1000; @@ -26,7 +26,7 @@ where E: CustomMsg, Q: CustomMsg, { - fn contract_info(&self, deps: Deps) -> StdResult { + fn collection_info(&self, deps: Deps) -> StdResult { self.collection_info.load(deps.storage) } @@ -251,7 +251,9 @@ where pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::Minter {} => to_json_binary(&self.minter(deps)?), - QueryMsg::ContractInfo {} => to_json_binary(&self.contract_info(deps)?), + #[allow(deprecated)] + QueryMsg::ContractInfo {} => to_json_binary(&self.collection_info(deps)?), + QueryMsg::CollectionInfo {} => to_json_binary(&self.collection_info(deps)?), QueryMsg::NftInfo { token_id } => to_json_binary(&self.nft_info(deps, token_id)?), QueryMsg::OwnerOf { token_id, @@ -354,7 +356,7 @@ fn parse_approval(item: StdResult<(Addr, Expiration)>) -> StdResult( block: &BlockInfo, - info: &TokenInfo, + info: &NftInfo, include_expired: bool, ) -> Vec { info.approvals diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 6c5a029cc..590b77694 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -5,7 +5,7 @@ use std::marker::PhantomData; use cosmwasm_std::{Addr, BlockInfo, CustomMsg, StdResult, Storage}; -use cw721::{ContractInfoResponse, Cw721, Expiration}; +use cw721::{CollectionInfoResponse, Cw721, Expiration}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; pub struct Cw721Contract<'a, T, C, E, Q> @@ -14,11 +14,11 @@ where Q: CustomMsg, E: CustomMsg, { - pub collection_info: Item<'a, ContractInfoResponse>, + pub collection_info: Item<'a, CollectionInfoResponse>, pub token_count: Item<'a, u64>, /// Stored as (granter, operator) giving operator full control over granter's account pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, - pub nft_info: IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a, T>>, + pub nft_info: IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, T>>, pub withdraw_address: Item<'a, String>, pub(crate) _custom_response: PhantomData, @@ -100,8 +100,11 @@ where } } +#[deprecated(since = "0.19.0", note = "Please use NftInfo")] +pub type TokenInfo = NftInfo; + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct TokenInfo { +pub struct NftInfo { /// The owner of the newly minted NFT pub owner: Addr, /// Approvals are stored here, as we clear them all upon transfer and cannot accumulate much @@ -134,19 +137,19 @@ pub struct TokenIndexes<'a, T> where T: Serialize + DeserializeOwned + Clone, { - pub owner: MultiIndex<'a, Addr, TokenInfo, String>, + pub owner: MultiIndex<'a, Addr, NftInfo, String>, } -impl<'a, T> IndexList> for TokenIndexes<'a, T> +impl<'a, T> IndexList> for TokenIndexes<'a, T> where T: Serialize + DeserializeOwned + Clone, { - fn get_indexes(&'_ self) -> Box>> + '_> { - let v: Vec<&dyn Index>> = vec![&self.owner]; + fn get_indexes(&'_ self) -> Box>> + '_> { + let v: Vec<&dyn Index>> = vec![&self.owner]; Box::new(v.into_iter()) } } -pub fn token_owner_idx(_pk: &[u8], d: &TokenInfo) -> Addr { +pub fn token_owner_idx(_pk: &[u8], d: &NftInfo) -> Addr { d.owner.clone() } diff --git a/contracts/cw721-expiration/src/contract_tests.rs b/contracts/cw721-expiration/src/contract_tests.rs index 7cfe8f501..65b7087dd 100644 --- a/contracts/cw721-expiration/src/contract_tests.rs +++ b/contracts/cw721-expiration/src/contract_tests.rs @@ -7,8 +7,8 @@ use cosmwasm_std::{ }; use cw721::{ - Approval, ApprovalResponse, ContractInfoResponse, Cw721ReceiveMsg, Expiration, NftInfoResponse, - OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + Approval, ApprovalResponse, CollectionInfoResponse, Cw721ReceiveMsg, Expiration, + NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; use cw_ownable::OwnershipError; @@ -64,7 +64,7 @@ fn proper_instantiation() { let info = contract.contract_info(deps.as_ref()).unwrap(); assert_eq!( info, - ContractInfoResponse { + CollectionInfoResponse { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), } diff --git a/contracts/cw721-expiration/src/lib.rs b/contracts/cw721-expiration/src/lib.rs index 29b0027f9..2fdb4575b 100644 --- a/contracts/cw721-expiration/src/lib.rs +++ b/contracts/cw721-expiration/src/lib.rs @@ -16,7 +16,10 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub type MinterResponse = cw721_base::msg::MinterResponse; pub type Extension = Option; -pub type TokenInfo = cw721_base::state::TokenInfo; +pub type NftInfo = cw721_base::state::NftInfo; + +#[deprecated(since = "0.19.0", note = "Please use NftInfo")] +pub type TokenInfo = NftInfo; pub mod entry { use crate::{ diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index 5aa019922..b0f5da375 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -73,10 +73,14 @@ pub enum QueryMsg { #[returns(cw721::NumTokensResponse)] NumTokens {}, + #[deprecated(since = "0.19.0", note = "Please use CollectionInfo instead")] + #[returns(cw721::CollectionInfoResponse)] + ContractInfo {}, + /// With MetaData Extension. /// Returns top-level metadata about the contract - #[returns(cw721::ContractInfoResponse)] - ContractInfo {}, + #[returns(cw721::CollectionInfoResponse)] + CollectionInfo {}, /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index 208a297eb..2eb6a60c5 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{to_json_binary, Addr, Binary, Deps, Env, StdResult}; use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, Cw721Query, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, Cw721Query, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; @@ -12,7 +12,9 @@ impl<'a> Cw721ExpirationContract<'a> { pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> Result { match msg { QueryMsg::Minter {} => Ok(to_json_binary(&self.minter(deps)?)?), - QueryMsg::ContractInfo {} => Ok(to_json_binary(&self.contract_info(deps)?)?), + #[allow(deprecated)] + QueryMsg::ContractInfo {} => Ok(to_json_binary(&self.collection_info(deps)?)?), + QueryMsg::CollectionInfo {} => Ok(to_json_binary(&self.collection_info(deps)?)?), QueryMsg::NftInfo { token_id, include_invalid, @@ -133,8 +135,12 @@ impl<'a> Cw721ExpirationContract<'a> { // queries impl<'a> Cw721ExpirationContract<'a> { - pub fn contract_info(&self, deps: Deps) -> StdResult { - self.base_contract.contract_info(deps) + pub fn contract_info(&self, deps: Deps) -> StdResult { + self.base_contract.collection_info(deps) + } + + pub fn collection_info(&self, deps: Deps) -> StdResult { + self.base_contract.collection_info.load(deps.storage) } pub fn num_tokens(&self, deps: Deps) -> StdResult { diff --git a/contracts/cw721-non-transferable/examples/schema.rs b/contracts/cw721-non-transferable/examples/schema.rs index 5d11328a0..9cd8ed434 100644 --- a/contracts/cw721-non-transferable/examples/schema.rs +++ b/contracts/cw721-non-transferable/examples/schema.rs @@ -3,9 +3,11 @@ use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; +#[allow(deprecated)] use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, Cw721ExecuteMsg, - NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, + ContractInfoResponse, Cw721ExecuteMsg, NftInfoResponse, NumTokensResponse, OperatorsResponse, + OwnerOfResponse, TokensResponse, }; use cw721_non_transferable::{Extension, InstantiateMsg, MinterResponse, QueryMsg}; @@ -26,7 +28,9 @@ fn main() { export_schema(&schema_for!(ApprovalResponse), &out_dir); export_schema(&schema_for!(ApprovalsResponse), &out_dir); export_schema(&schema_for!(OperatorsResponse), &out_dir); + #[allow(deprecated)] export_schema(&schema_for!(ContractInfoResponse), &out_dir); + export_schema(&schema_for!(CollectionInfoResponse), &out_dir); export_schema(&schema_for!(MinterResponse), &out_dir); export_schema_with_title( &schema_for!(NftInfoResponse), diff --git a/contracts/cw721-non-transferable/src/msg.rs b/contracts/cw721-non-transferable/src/msg.rs index 703a9a7d6..81e5ee761 100644 --- a/contracts/cw721-non-transferable/src/msg.rs +++ b/contracts/cw721-non-transferable/src/msg.rs @@ -34,7 +34,9 @@ pub enum QueryMsg { limit: Option, }, NumTokens {}, + #[deprecated(since = "0.19.0", note = "Please use CollectionInfo instead")] ContractInfo {}, + CollectionInfo {}, NftInfo { token_id: String, }, @@ -65,7 +67,9 @@ impl From for Cw721QueryMsg { include_expired, }, QueryMsg::NumTokens {} => Cw721QueryMsg::NumTokens {}, - QueryMsg::ContractInfo {} => Cw721QueryMsg::ContractInfo {}, + #[allow(deprecated)] + QueryMsg::ContractInfo {} => Cw721QueryMsg::CollectionInfo {}, + QueryMsg::CollectionInfo {} => Cw721QueryMsg::CollectionInfo {}, QueryMsg::NftInfo { token_id } => Cw721QueryMsg::NftInfo { token_id }, QueryMsg::AllNftInfo { token_id, diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 2999afa75..5255b1f4a 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -4,10 +4,11 @@ use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; use cosmwasm_std::Empty; +#[allow(deprecated)] use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, Cw721ExecuteMsg, - Cw721QueryMsg, Cw721ReceiveMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, - OperatorsResponse, OwnerOfResponse, TokensResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, + ContractInfoResponse, Cw721ExecuteMsg, Cw721QueryMsg, Cw721ReceiveMsg, NftInfoResponse, + NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; type Extension = Option; @@ -30,7 +31,9 @@ fn main() { export_schema(&schema_for!(ApprovalsResponse), &out_dir); export_schema(&schema_for!(OperatorResponse), &out_dir); export_schema(&schema_for!(OperatorsResponse), &out_dir); + #[allow(deprecated)] export_schema(&schema_for!(ContractInfoResponse), &out_dir); + export_schema(&schema_for!(CollectionInfoResponse), &out_dir); export_schema(&schema_for!(OwnerOfResponse), &out_dir); export_schema_with_title( &schema_for!(NftInfoResponse), diff --git a/packages/cw721/src/lib.rs b/packages/cw721/src/lib.rs index 312189b64..dd76ef9f5 100644 --- a/packages/cw721/src/lib.rs +++ b/packages/cw721/src/lib.rs @@ -6,10 +6,11 @@ mod traits; pub use cw_utils::Expiration; pub use crate::msg::Cw721ExecuteMsg; +#[allow(deprecated)] pub use crate::query::{ - AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, - Cw721QueryMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, - OwnerOfResponse, TokensResponse, + AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, + ContractInfoResponse, Cw721QueryMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, + OperatorsResponse, OwnerOfResponse, TokensResponse, }; pub use crate::receiver::Cw721ReceiveMsg; pub use crate::traits::{Cw721, Cw721Execute, Cw721Query}; diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index f336c7530..8e82a7934 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -116,8 +116,12 @@ pub struct NumTokensResponse { pub count: u64, } +#[deprecated(since = "0.19.0", note = "Please use CollectionInfoResponse instead")] +#[allow(dead_code)] +pub type ContractInfoResponse = CollectionInfoResponse; + #[cw_serde] -pub struct ContractInfoResponse { +pub struct CollectionInfoResponse { pub name: String, pub symbol: String, } diff --git a/packages/cw721/src/traits.rs b/packages/cw721/src/traits.rs index 1022a0eb3..33d33610c 100644 --- a/packages/cw721/src/traits.rs +++ b/packages/cw721/src/traits.rs @@ -2,8 +2,9 @@ use serde::de::DeserializeOwned; use serde::Serialize; use crate::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, NftInfoResponse, - NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, + NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, + TokensResponse, }; use cosmwasm_std::{Binary, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; use cw_utils::Expiration; @@ -93,7 +94,7 @@ where // TODO: use custom error? // How to handle the two derived error types? - fn contract_info(&self, deps: Deps) -> StdResult; + fn collection_info(&self, deps: Deps) -> StdResult; fn num_tokens(&self, deps: Deps) -> StdResult; From 98b73004ec23588fb59076a9e337e0806764c8e9 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 27 Feb 2024 15:13:06 +0100 Subject: [PATCH 03/47] cargo schema --- .../schema/cw2981-royalties.json | 38 +++++++++++++++++-- contracts/cw721-base/schema/cw721-base.json | 38 +++++++++++++++++-- .../schema/cw721-expiration.json | 38 +++++++++++++++++-- .../schema/cw721-fixed-price.json | 2 +- .../schema/cw721-metadata-onchain.json | 38 +++++++++++++++++-- ...nse.json => collection_info_response.json} | 2 +- .../schema/query_msg.json | 14 +++++++ .../schema/cw721-receiver-tester.json | 2 +- 8 files changed, 157 insertions(+), 15 deletions(-) rename contracts/cw721-non-transferable/schema/{contract_info_response.json => collection_info_response.json} (87%) diff --git a/contracts/cw2981-royalties/schema/cw2981-royalties.json b/contracts/cw2981-royalties/schema/cw2981-royalties.json index 5ac8e2923..7bb9c8d7b 100644 --- a/contracts/cw2981-royalties/schema/cw2981-royalties.json +++ b/contracts/cw2981-royalties/schema/cw2981-royalties.json @@ -1,6 +1,6 @@ { "contract_name": "cw2981-royalties", - "contract_version": "0.18.0", + "contract_version": "0.19.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -798,7 +798,7 @@ "additionalProperties": false }, { - "description": "With MetaData Extension. Returns top-level metadata about the contract", + "deprecated": true, "type": "object", "required": [ "contract_info" @@ -811,6 +811,20 @@ }, "additionalProperties": false }, + { + "description": "With MetaData Extension. Returns top-level metadata about the contract", + "type": "object", + "required": [ + "collection_info" + ], + "properties": { + "collection_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", "type": "object", @@ -1565,9 +1579,27 @@ } } }, + "collection_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfoResponse", + "type": "object", + "required": [ + "name", + "symbol" + ], + "properties": { + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + }, + "additionalProperties": false + }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ContractInfoResponse", + "title": "CollectionInfoResponse", "type": "object", "required": [ "name", diff --git a/contracts/cw721-base/schema/cw721-base.json b/contracts/cw721-base/schema/cw721-base.json index 5b2403b29..297d9bab8 100644 --- a/contracts/cw721-base/schema/cw721-base.json +++ b/contracts/cw721-base/schema/cw721-base.json @@ -1,6 +1,6 @@ { "contract_name": "cw721-base", - "contract_version": "0.18.0", + "contract_version": "0.19.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -695,7 +695,7 @@ "additionalProperties": false }, { - "description": "With MetaData Extension. Returns top-level metadata about the contract", + "deprecated": true, "type": "object", "required": [ "contract_info" @@ -708,6 +708,20 @@ }, "additionalProperties": false }, + { + "description": "With MetaData Extension. Returns top-level metadata about the contract", + "type": "object", + "required": [ + "collection_info" + ], + "properties": { + "collection_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", "type": "object", @@ -1374,9 +1388,27 @@ } } }, + "collection_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfoResponse", + "type": "object", + "required": [ + "name", + "symbol" + ], + "properties": { + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + }, + "additionalProperties": false + }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ContractInfoResponse", + "title": "CollectionInfoResponse", "type": "object", "required": [ "name", diff --git a/contracts/cw721-expiration/schema/cw721-expiration.json b/contracts/cw721-expiration/schema/cw721-expiration.json index 3da3c0405..f10d77105 100644 --- a/contracts/cw721-expiration/schema/cw721-expiration.json +++ b/contracts/cw721-expiration/schema/cw721-expiration.json @@ -1,6 +1,6 @@ { "contract_name": "cw721-expiration", - "contract_version": "0.18.0", + "contract_version": "0.19.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -725,7 +725,7 @@ "additionalProperties": false }, { - "description": "With MetaData Extension. Returns top-level metadata about the contract", + "deprecated": true, "type": "object", "required": [ "contract_info" @@ -738,6 +738,20 @@ }, "additionalProperties": false }, + { + "description": "With MetaData Extension. Returns top-level metadata about the contract", + "type": "object", + "required": [ + "collection_info" + ], + "properties": { + "collection_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", "type": "object", @@ -1419,9 +1433,27 @@ } } }, + "collection_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfoResponse", + "type": "object", + "required": [ + "name", + "symbol" + ], + "properties": { + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + }, + "additionalProperties": false + }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ContractInfoResponse", + "title": "CollectionInfoResponse", "type": "object", "required": [ "name", diff --git a/contracts/cw721-fixed-price/schema/cw721-fixed-price.json b/contracts/cw721-fixed-price/schema/cw721-fixed-price.json index 694ec7f85..ff2ae78b8 100644 --- a/contracts/cw721-fixed-price/schema/cw721-fixed-price.json +++ b/contracts/cw721-fixed-price/schema/cw721-fixed-price.json @@ -1,6 +1,6 @@ { "contract_name": "cw721-fixed-price", - "contract_version": "0.18.0", + "contract_version": "0.19.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json b/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json index c6dfa6cd1..75e86dd1b 100644 --- a/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json +++ b/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json @@ -1,6 +1,6 @@ { "contract_name": "cw721-metadata-onchain", - "contract_version": "0.18.0", + "contract_version": "0.19.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -782,7 +782,7 @@ "additionalProperties": false }, { - "description": "With MetaData Extension. Returns top-level metadata about the contract", + "deprecated": true, "type": "object", "required": [ "contract_info" @@ -795,6 +795,20 @@ }, "additionalProperties": false }, + { + "description": "With MetaData Extension. Returns top-level metadata about the contract", + "type": "object", + "required": [ + "collection_info" + ], + "properties": { + "collection_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", "type": "object", @@ -1461,9 +1475,27 @@ } } }, + "collection_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfoResponse", + "type": "object", + "required": [ + "name", + "symbol" + ], + "properties": { + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + }, + "additionalProperties": false + }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ContractInfoResponse", + "title": "CollectionInfoResponse", "type": "object", "required": [ "name", diff --git a/contracts/cw721-non-transferable/schema/contract_info_response.json b/contracts/cw721-non-transferable/schema/collection_info_response.json similarity index 87% rename from contracts/cw721-non-transferable/schema/contract_info_response.json rename to contracts/cw721-non-transferable/schema/collection_info_response.json index 4a805a825..84822f8b1 100644 --- a/contracts/cw721-non-transferable/schema/contract_info_response.json +++ b/contracts/cw721-non-transferable/schema/collection_info_response.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ContractInfoResponse", + "title": "CollectionInfoResponse", "type": "object", "required": [ "name", diff --git a/contracts/cw721-non-transferable/schema/query_msg.json b/contracts/cw721-non-transferable/schema/query_msg.json index ffbb412b3..33853b647 100644 --- a/contracts/cw721-non-transferable/schema/query_msg.json +++ b/contracts/cw721-non-transferable/schema/query_msg.json @@ -155,6 +155,7 @@ "additionalProperties": false }, { + "deprecated": true, "type": "object", "required": [ "contract_info" @@ -167,6 +168,19 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "collection_info" + ], + "properties": { + "collection_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/contracts/cw721-receiver-tester/schema/cw721-receiver-tester.json b/contracts/cw721-receiver-tester/schema/cw721-receiver-tester.json index 639e533cb..a26f45a43 100644 --- a/contracts/cw721-receiver-tester/schema/cw721-receiver-tester.json +++ b/contracts/cw721-receiver-tester/schema/cw721-receiver-tester.json @@ -1,6 +1,6 @@ { "contract_name": "cw721-receiver-tester", - "contract_version": "0.18.0", + "contract_version": "0.19.0", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", From 5fd2f3b654c2fcba5911e5e9c7bff32261e163cd Mon Sep 17 00:00:00 2001 From: mr-t Date: Wed, 28 Feb 2024 10:58:34 +0100 Subject: [PATCH 04/47] change storage keys for collection info and nft info, consider in migration and keep legacy data for backward migration --- contracts/cw721-base/src/lib.rs | 256 +++++++++++++++++++-- contracts/cw721-base/src/query.rs | 4 +- contracts/cw721-base/src/state.rs | 8 +- contracts/cw721-base/src/upgrades/mod.rs | 1 - contracts/cw721-base/src/upgrades/v0_17.rs | 27 --- 5 files changed, 249 insertions(+), 47 deletions(-) delete mode 100644 contracts/cw721-base/src/upgrades/mod.rs delete mode 100644 contracts/cw721-base/src/upgrades/v0_17.rs diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index b7b567ed8..a76556a10 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -4,7 +4,6 @@ pub mod helpers; pub mod msg; mod query; pub mod state; -pub mod upgrades; #[cfg(test)] mod contract_tests; @@ -32,18 +31,18 @@ pub type Extension = Option; pub const CONTRACT_NAME: &str = "crates.io:cw721-base"; pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -// currently we only support migrating from 0.16.0. this is ok for now because -// we have not released any 0.16.x where x != 0 -// -// TODO: parse semvar so that any version 0.16.x can be migrated from -pub const EXPECTED_FROM_VERSION: &str = "0.16.0"; - pub mod entry { + use self::state::{token_owner_idx, NftInfo, TokenIndexes}; + use super::*; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; - use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; + use cosmwasm_std::{ + Addr, Api, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult, Storage, + }; + use cw721::CollectionInfoResponse; + use cw_storage_plus::{IndexedMap, Item, MultiIndex}; // This makes a conscious choice on the various generics used by the contract #[cfg_attr(not(feature = "library"), entry_point)] @@ -77,23 +76,122 @@ pub mod entry { } #[cfg_attr(not(feature = "library"), entry_point)] - pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result { + pub fn migrate(deps: DepsMut, env: Env, msg: Empty) -> Result { + let response = migrate_version(deps.storage, &env, &msg)?; + let response = migrate_legacy_minter(deps.storage, deps.api, &env, &msg, response)?; + let response = migrate_legacy_contract_info(deps.storage, &env, &msg, response)?; + migrate_legacy_tokens(deps.storage, &env, &msg, response) + } + + pub fn migrate_version( + storage: &mut dyn Storage, + _env: &Env, + _msg: &Empty, + ) -> Result { // make sure the correct contract is being upgraded, and it's being // upgraded from the correct version. - cw2::assert_contract_version(deps.as_ref().storage, CONTRACT_NAME, EXPECTED_FROM_VERSION)?; + let response = Response::::default() + .add_attribute("from_version", cw2::get_contract_version(storage)?.version) + .add_attribute("to_version", CONTRACT_VERSION); // update contract version - cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + cw2::set_contract_version(storage, CONTRACT_NAME, CONTRACT_VERSION)?; + Ok(response) + } + + /// Migrates only in case ownership is not present + pub fn migrate_legacy_minter( + storage: &mut dyn Storage, + api: &dyn Api, + _env: &Env, + _msg: &Empty, + response: Response, + ) -> Result { + let owner_store: Item> = Item::new("ownership"); // TODO: cw_ownable does not support may_load, change this once it has support + match owner_store.may_load(storage)? { + Some(_) => Ok(response), + None => { + let legacy_minter_store: Item = Item::new("minter"); + let legacy_minter = legacy_minter_store.load(storage)?; + let ownership = + cw_ownable::initialize_owner(storage, api, Some(legacy_minter.as_str()))?; + Ok(response + .add_attribute("old_minter", legacy_minter) + .add_attributes(ownership.into_attributes())) + } + } + } - // perform the upgrade - upgrades::v0_17::migrate::(deps) + /// Migrates only in case collection_info is not present + pub fn migrate_legacy_contract_info( + storage: &mut dyn Storage, + _env: &Env, + _msg: &Empty, + response: Response, + ) -> Result { + let contract = Cw721Contract::::default(); + match contract.collection_info.may_load(storage)? { + Some(_) => Ok(response), + None => { + let legacy_collection_info_store: Item = + Item::new("nft_info"); + let legacy_collection_info = legacy_collection_info_store.load(storage)?; + contract + .collection_info + .save(storage, &legacy_collection_info)?; + Ok(response + .add_attribute("migrated collection name", legacy_collection_info.name) + .add_attribute("migrated collection symbol", legacy_collection_info.symbol)) + } + } + } + + /// Migrates only in case collection_info is not present + pub fn migrate_legacy_tokens( + storage: &mut dyn Storage, + _env: &Env, + _msg: &Empty, + response: Response, + ) -> Result { + let contract = Cw721Contract::::default(); + match contract.nft_info.is_empty(storage) { + false => Ok(response), + true => { + let indexes = TokenIndexes { + owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"), + }; + let legacy_tokens_store: IndexedMap< + &str, + NftInfo, + TokenIndexes, + > = IndexedMap::new("tokens", indexes); + let keys = legacy_tokens_store + .keys(storage, None, None, Order::Ascending) + .collect::>>()?; + for key in keys { + let legacy_token = legacy_tokens_store.load(storage, &key)?; + contract.nft_info.save(storage, &key, &legacy_token)?; + } + Ok(response) + } + } } } #[cfg(test)] mod tests { - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info}, + Addr, Order, StdError, StdResult, + }; use cw2::ContractVersion; + use cw721::{CollectionInfoResponse, Cw721Query}; + use cw_storage_plus::{IndexedMap, Item, MultiIndex}; + + use crate::{ + query::MAX_LIMIT, + state::{token_owner_idx, NftInfo, TokenIndexes}, + }; use super::*; @@ -154,4 +252,134 @@ mod tests { .map(|a| a.into_string()); assert_eq!(minter, Some("owner".to_string())); } + + #[test] + fn test_migrate() { + let mut deps = mock_dependencies(); + + let env = mock_env(); + use cw721_base_016 as v16; + v16::entry::instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &[]), + v16::InstantiateMsg { + name: "legacy_name".into(), + symbol: "legacy_symbol".into(), + minter: "legacy_minter".into(), + }, + ) + .unwrap(); + + // mint 200 NFTs before migration + for i in 0..200 { + let info = mock_info("legacy_minter", &[]); + let msg = v16::ExecuteMsg::Mint(v16::msg::MintMsg { + token_id: i.to_string(), + owner: "owner".into(), + token_uri: None, + extension: None, + }); + v16::entry::execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + } + + // assert new data before migration: + // - ownership and collection info throws NotFound Error + cw_ownable::get_ownership(deps.as_ref().storage).unwrap_err(); + let contract = Cw721Contract::::default(); + contract.collection_info(deps.as_ref()).unwrap_err(); + // - no tokens + let all_tokens = contract + .all_tokens(deps.as_ref(), None, Some(MAX_LIMIT)) + .unwrap(); + assert_eq!(all_tokens.tokens.len(), 0); + + // assert legacy data before migration: + // - version + let version = cw2::get_contract_version(deps.as_ref().storage) + .unwrap() + .version; + assert_eq!(version, "0.16.0"); + // - legacy minter is set + let legacy_minter_store: Item = Item::new("minter"); + let legacy_minter = legacy_minter_store.load(deps.as_ref().storage).unwrap(); + assert_eq!(legacy_minter, "legacy_minter"); + // - legacy collection info is set + let legacy_collection_info_store: Item = Item::new("nft_info"); + let legacy_collection_info = legacy_collection_info_store + .load(deps.as_ref().storage) + .unwrap(); + assert_eq!(legacy_collection_info.name, "legacy_name"); + assert_eq!(legacy_collection_info.symbol, "legacy_symbol"); + // - legacy tokens are set + let indexes = TokenIndexes { + owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"), + }; + let legacy_tokens_store: IndexedMap<&str, NftInfo, TokenIndexes> = + IndexedMap::new("tokens", indexes); + let keys = legacy_tokens_store + .keys(deps.as_ref().storage, None, None, Order::Ascending) + .collect::>>() + .unwrap(); + assert_eq!(keys.len(), 200); + for key in keys { + let legacy_token = legacy_tokens_store + .load(deps.as_ref().storage, &key) + .unwrap(); + assert_eq!(legacy_token.owner.as_str(), "owner"); + } + + entry::migrate(deps.as_mut(), env, Empty {}).unwrap(); + + // version + let version = cw2::get_contract_version(deps.as_ref().storage) + .unwrap() + .version; + assert_eq!(version, CONTRACT_VERSION); + assert_ne!(version, "0.16.0"); + + // assert ownership + let ownership = cw_ownable::get_ownership(deps.as_ref().storage) + .unwrap() + .owner + .map(|a| a.into_string()); + assert_eq!(ownership, Some("legacy_minter".to_string())); + + // assert collection info + let collection_info = contract.collection_info(deps.as_ref()).unwrap(); + let legacy_contract_info = CollectionInfoResponse { + name: "legacy_name".to_string(), + symbol: "legacy_symbol".to_string(), + }; + assert_eq!(collection_info, legacy_contract_info); + + // assert tokens + let all_tokens = contract + .all_tokens(deps.as_ref(), None, Some(MAX_LIMIT)) + .unwrap(); + assert_eq!(all_tokens.tokens.len(), 200); + + // assert legacy data is still there (allowing backward migration in case of issues) + // - minter + let legacy_minter = legacy_minter_store.load(deps.as_ref().storage).unwrap(); + assert_eq!(legacy_minter, "legacy_minter"); + // - collection info + let legacy_collection_info = legacy_collection_info_store + .load(deps.as_ref().storage) + .unwrap(); + assert_eq!(legacy_collection_info.name, "legacy_name"); + assert_eq!(legacy_collection_info.symbol, "legacy_symbol"); + // - tokens + let keys = legacy_tokens_store + .keys(deps.as_ref().storage, None, None, Order::Ascending) + .collect::>>() + .unwrap(); + assert_eq!(keys.len(), 200); + for key in keys { + let legacy_token = legacy_tokens_store + .load(deps.as_ref().storage, &key) + .unwrap(); + assert_eq!(legacy_token.owner.as_str(), "owner"); + } + } } diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index 496944d24..e42942df6 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -16,8 +16,8 @@ use cw_utils::maybe_addr; use crate::msg::{MinterResponse, QueryMsg}; use crate::state::{Approval, Cw721Contract, NftInfo}; -const DEFAULT_LIMIT: u32 = 10; -const MAX_LIMIT: u32 = 1000; +pub const DEFAULT_LIMIT: u32 = 10; +pub const MAX_LIMIT: u32 = 1000; impl<'a, T, C, E, Q> Cw721Query for Cw721Contract<'a, T, C, E, Q> where diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 590b77694..cc6f5aedc 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -14,10 +14,12 @@ where Q: CustomMsg, E: CustomMsg, { + /// Note: do not use deprecated/legacy key "nft_info"! pub collection_info: Item<'a, CollectionInfoResponse>, pub token_count: Item<'a, u64>, /// Stored as (granter, operator) giving operator full control over granter's account pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, + /// Note: do not use deprecated/legacy keys "tokens" and "tokens__owner"! pub nft_info: IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, T>>, pub withdraw_address: Item<'a, String>, @@ -44,11 +46,11 @@ where { fn default() -> Self { Self::new( - "nft_info", + "collection_info", // Note: do not use deprecated/legacy key "nft_info" "num_tokens", "operators", - "tokens", - "tokens__owner", + "nft", // Note: do not use deprecated/legacy key "tokens" + "nft__owner", // Note: do not use deprecated/legacy key "tokens__owner" "withdraw_address", ) } diff --git a/contracts/cw721-base/src/upgrades/mod.rs b/contracts/cw721-base/src/upgrades/mod.rs deleted file mode 100644 index bdb01c30d..000000000 --- a/contracts/cw721-base/src/upgrades/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod v0_17; diff --git a/contracts/cw721-base/src/upgrades/v0_17.rs b/contracts/cw721-base/src/upgrades/v0_17.rs deleted file mode 100644 index c0bd63b98..000000000 --- a/contracts/cw721-base/src/upgrades/v0_17.rs +++ /dev/null @@ -1,27 +0,0 @@ -use cosmwasm_std::{CustomMsg, DepsMut, Response}; -use cw721_base_016 as v16; -use serde::{de::DeserializeOwned, Serialize}; - -use crate::ContractError; - -pub fn migrate(deps: DepsMut) -> Result, ContractError> -where - T: Serialize + DeserializeOwned + Clone, - Q: CustomMsg, - E: CustomMsg, -{ - // remove old minter info - let tract16 = v16::Cw721Contract::::default(); - let minter = tract16.minter.load(deps.storage)?; - tract16.minter.remove(deps.storage); - - // save new ownership info - let ownership = cw_ownable::initialize_owner(deps.storage, deps.api, Some(minter.as_str()))?; - - Ok(Response::new() - .add_attribute("action", "migrate") - .add_attribute("from_version", "0.16.0") - .add_attribute("to_version", "0.17.0") - .add_attribute("old_minter", minter) - .add_attributes(ownership.into_attributes())) -} From a2b5c12a4b29b70ef821614e8c0033afa0ddd495 Mon Sep 17 00:00:00 2001 From: mr-t Date: Wed, 28 Feb 2024 22:08:39 +0100 Subject: [PATCH 05/47] use PA repo for `cw-ownable` --- Cargo.lock | 129 ++++++++++++++++---------------- Cargo.toml | 2 +- contracts/cw721-base/src/lib.rs | 4 +- 3 files changed, 66 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d65787867..2eba40509 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.77" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "base16ct" @@ -27,9 +27,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -63,9 +63,9 @@ dependencies = [ [[package]] name = "bnum" -version = "0.8.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" +checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" [[package]] name = "byteorder" @@ -93,9 +93,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cosmwasm-crypto" -version = "1.5.0" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bb3c77c3b7ce472056968c745eb501c440fbc07be5004eba02782c35bfbbe3" +checksum = "9934c79e58d9676edfd592557dee765d2a6ef54c09d5aa2edb06156b00148966" dependencies = [ "digest 0.10.7", "ecdsa", @@ -107,18 +107,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.0" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea73e9162e6efde00018d55ed0061e93a108b5d6ec4548b4f8ce3c706249687" +checksum = "bc5e72e330bd3bdab11c52b5ecbdeb6a8697a004c57964caeb5d876f0b088b3c" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.0" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df41ea55f2946b6b43579659eec048cc2f66e8c8e2e3652fc5e5e476f673856" +checksum = "ac3e3a2136e2a60e8b6582f5dffca5d1a683ed77bf38537d330bc1dfccd69010" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -129,9 +129,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.0" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43609e92ce1b9368aa951b334dd354a2d0dd4d484931a5f83ae10e12a26c8ba9" +checksum = "f5d803bea6bd9ed61bd1ee0b4a2eb09ee20dbb539cc6e0b8795614d20952ebb1" dependencies = [ "proc-macro2", "quote", @@ -140,9 +140,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.0" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d6864742e3a7662d024b51a94ea81c9af21db6faea2f9a6d2232bb97c6e53e" +checksum = "ef8666e572a3a2519010dde88c04d16e9339ae751b56b2bb35081fe3f7d6be74" dependencies = [ "base64", "bech32", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -207,8 +207,7 @@ dependencies = [ [[package]] name = "cw-address-like" version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451a4691083a88a3c0630a8a88799e9d4cd6679b7ce8ff22b8da2873ff31d380" +source = "git+https://github.com/public-awesome/cw-plus-plus.git?branch=multiple_ownership#d40ce97bc1cc0e4870591ba8bf4a6160b1ae8b00" dependencies = [ "cosmwasm-std", ] @@ -225,7 +224,7 @@ dependencies = [ "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "derivative", - "itertools 0.12.0", + "itertools 0.12.1", "prost 0.12.3", "schemars", "serde", @@ -235,9 +234,8 @@ dependencies = [ [[package]] name = "cw-ownable" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093dfb4520c48b5848274dd88ea99e280a04bc08729603341c7fb0d758c74321" +version = "0.6.0" +source = "git+https://github.com/public-awesome/cw-plus-plus.git?branch=multiple_ownership#d40ce97bc1cc0e4870591ba8bf4a6160b1ae8b00" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -250,9 +248,8 @@ dependencies = [ [[package]] name = "cw-ownable-derive" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d3bf2e0f341bb6cc100d7d441d31cf713fbd3ce0c511f91e79f14b40a889af" +version = "0.6.0" +source = "git+https://github.com/public-awesome/cw-plus-plus.git?branch=multiple_ownership#d40ce97bc1cc0e4870591ba8bf4a6160b1ae8b00" dependencies = [ "proc-macro2", "quote", @@ -544,9 +541,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" @@ -579,9 +576,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "elliptic-curve" @@ -631,9 +628,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -695,9 +692,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -710,9 +707,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "k256" -version = "0.13.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", "ecdsa", @@ -724,9 +721,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.151" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "once_cell" @@ -752,9 +749,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -802,14 +799,14 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.52", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -841,9 +838,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "schemars" @@ -885,37 +882,37 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde-json-wasm" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +checksum = "9e9213a07d53faa0b8dd81e767a54a8188a242fdb9be99ab75ec576a774bfdd7" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.52", ] [[package]] @@ -931,9 +928,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -1009,9 +1006,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.43" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -1020,22 +1017,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.52" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.52" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.52", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c81542191..c422d777c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ cw721 = { version = "*", path = "./packages/cw721" } cw721-base = { version = "*", path = "./contracts/cw721-base" } cw721-base-016 = { version = "0.16.0", package = "cw721-base" } cw-multi-test = "^0.20" -cw-ownable = "^0.5" +cw-ownable = { git = "https://github.com/public-awesome/cw-plus-plus.git", branch = "multiple_ownership"} # TODO: switch to official https://github.com/larry0x/cw-plus-plus once merged cw-storage-plus = "^1.1" cw-utils = "^1.0" schemars = "^0.8" diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index a76556a10..247d22644 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -42,6 +42,7 @@ pub mod entry { Addr, Api, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult, Storage, }; use cw721::CollectionInfoResponse; + use cw_ownable::OWNERSHIP; use cw_storage_plus::{IndexedMap, Item, MultiIndex}; // This makes a conscious choice on the various generics used by the contract @@ -107,8 +108,7 @@ pub mod entry { _msg: &Empty, response: Response, ) -> Result { - let owner_store: Item> = Item::new("ownership"); // TODO: cw_ownable does not support may_load, change this once it has support - match owner_store.may_load(storage)? { + match OWNERSHIP.item.may_load(storage)? { Some(_) => Ok(response), None => { let legacy_minter_store: Item = Item::new("minter"); From b2f73a0273169f6633addd26676f48922e953865 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 29 Feb 2024 13:25:01 +0100 Subject: [PATCH 06/47] rename generics and make more readable --- contracts/cw721-base/src/execute.rs | 91 +++++++++++++++------------ contracts/cw721-base/src/helpers.rs | 17 +++-- contracts/cw721-base/src/lib.rs | 2 +- contracts/cw721-base/src/msg.rs | 14 ++--- contracts/cw721-base/src/query.rs | 44 ++++++++----- contracts/cw721-base/src/state.rs | 87 +++++++++++++++---------- contracts/cw721-expiration/src/lib.rs | 2 +- packages/cw721/src/query.rs | 8 +-- packages/cw721/src/receiver.rs | 7 ++- packages/cw721/src/traits.rs | 35 ++++++----- 10 files changed, 181 insertions(+), 126 deletions(-) diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 05b1381b7..3f0486bae 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -12,12 +12,13 @@ use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg}; use crate::state::{Approval, Cw721Contract, NftInfo}; -impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> +impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> + Cw721Contract<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> where - T: Serialize + DeserializeOwned + Clone, - C: CustomMsg, - E: CustomMsg, - Q: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, { pub fn instantiate( &self, @@ -25,7 +26,7 @@ where _env: Env, info: MessageInfo, msg: InstantiateMsg, - ) -> Result, ContractError> { + ) -> Result, ContractError> { let contract_info = CollectionInfoResponse { name: msg.name, symbol: msg.symbol, @@ -50,8 +51,8 @@ where deps: DepsMut, env: Env, info: MessageInfo, - msg: ExecuteMsg, - ) -> Result, ContractError> { + msg: ExecuteMsg, + ) -> Result, ContractError> { match msg { ExecuteMsg::Mint { token_id, @@ -95,12 +96,13 @@ where } // TODO pull this into some sort of trait extension?? -impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> +impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> + Cw721Contract<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> where - T: Serialize + DeserializeOwned + Clone, - C: CustomMsg, - E: CustomMsg, - Q: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, { pub fn mint( &self, @@ -109,8 +111,8 @@ where token_id: String, owner: String, token_uri: Option, - extension: T, - ) -> Result, ContractError> { + extension: TMetadata, + ) -> Result, ContractError> { cw_ownable::assert_owner(deps.storage, &info.sender)?; // create the token @@ -140,7 +142,7 @@ where env: Env, info: MessageInfo, action: cw_ownable::Action, - ) -> Result, ContractError> { + ) -> Result, ContractError> { let ownership = cw_ownable::update_ownership(deps, &env.block, &info.sender, action)?; Ok(Response::new().add_attributes(ownership.into_attributes())) } @@ -150,7 +152,7 @@ where deps: DepsMut, sender: &Addr, address: String, - ) -> Result, ContractError> { + ) -> Result, ContractError> { cw_ownable::assert_owner(deps.storage, sender)?; deps.api.addr_validate(&address)?; self.withdraw_address.save(deps.storage, &address)?; @@ -163,7 +165,7 @@ where &self, storage: &mut dyn Storage, sender: &Addr, - ) -> Result, ContractError> { + ) -> Result, ContractError> { cw_ownable::assert_owner(storage, sender)?; let address = self.withdraw_address.may_load(storage)?; match address { @@ -181,7 +183,7 @@ where &self, storage: &mut dyn Storage, amount: &Coin, - ) -> Result, ContractError> { + ) -> Result, ContractError> { let address = self.withdraw_address.may_load(storage)?; match address { Some(address) => { @@ -200,12 +202,20 @@ where } } -impl<'a, T, C, E, Q> Cw721Execute for Cw721Contract<'a, T, C, E, Q> +impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> + Cw721Execute + for Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + > where - T: Serialize + DeserializeOwned + Clone, - C: CustomMsg, - E: CustomMsg, - Q: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, { type Err = ContractError; @@ -216,7 +226,7 @@ where info: MessageInfo, recipient: String, token_id: String, - ) -> Result, ContractError> { + ) -> Result, ContractError> { self._transfer_nft(deps, &env, &info, &recipient, &token_id)?; Ok(Response::new() @@ -234,7 +244,7 @@ where contract: String, token_id: String, msg: Binary, - ) -> Result, ContractError> { + ) -> Result, ContractError> { // Transfer token self._transfer_nft(deps, &env, &info, &contract, &token_id)?; @@ -261,7 +271,7 @@ where spender: String, token_id: String, expires: Option, - ) -> Result, ContractError> { + ) -> Result, ContractError> { self._update_approvals(deps, &env, &info, &spender, &token_id, true, expires)?; Ok(Response::new() @@ -278,7 +288,7 @@ where info: MessageInfo, spender: String, token_id: String, - ) -> Result, ContractError> { + ) -> Result, ContractError> { self._update_approvals(deps, &env, &info, &spender, &token_id, false, None)?; Ok(Response::new() @@ -295,7 +305,7 @@ where info: MessageInfo, operator: String, expires: Option, - ) -> Result, ContractError> { + ) -> Result, ContractError> { // reject expired data as invalid let expires = expires.unwrap_or_default(); if expires.is_expired(&env.block) { @@ -319,7 +329,7 @@ where _env: Env, info: MessageInfo, operator: String, - ) -> Result, ContractError> { + ) -> Result, ContractError> { let operator_addr = deps.api.addr_validate(&operator)?; self.operators .remove(deps.storage, (&info.sender, &operator_addr)); @@ -336,7 +346,7 @@ where env: Env, info: MessageInfo, token_id: String, - ) -> Result, ContractError> { + ) -> Result, ContractError> { let token = self.nft_info.load(deps.storage, &token_id)?; self.check_can_send(deps.as_ref(), &env, &info, &token)?; @@ -351,12 +361,13 @@ where } // helpers -impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> +impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> + Cw721Contract<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> where - T: Serialize + DeserializeOwned + Clone, - C: CustomMsg, - E: CustomMsg, - Q: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, { pub fn _transfer_nft( &self, @@ -365,7 +376,7 @@ where info: &MessageInfo, recipient: &str, token_id: &str, - ) -> Result, ContractError> { + ) -> Result, ContractError> { let mut token = self.nft_info.load(deps.storage, token_id)?; // ensure we have permissions self.check_can_send(deps.as_ref(), env, info, &token)?; @@ -387,7 +398,7 @@ where // if add == false, remove. if add == true, remove then set with this expiration add: bool, expires: Option, - ) -> Result, ContractError> { + ) -> Result, ContractError> { let mut token = self.nft_info.load(deps.storage, token_id)?; // ensure we have permissions self.check_can_approve(deps.as_ref(), env, info, &token)?; @@ -421,7 +432,7 @@ where deps: Deps, env: &Env, info: &MessageInfo, - token: &NftInfo, + token: &NftInfo, ) -> Result<(), ContractError> { // owner can approve if token.owner == info.sender { @@ -449,7 +460,7 @@ where deps: Deps, env: &Env, info: &MessageInfo, - token: &NftInfo, + token: &NftInfo, ) -> Result<(), ContractError> { // owner can send if token.owner == info.sender { diff --git a/contracts/cw721-base/src/helpers.rs b/contracts/cw721-base/src/helpers.rs index 9c3761886..5aae2dc8d 100644 --- a/contracts/cw721-base/src/helpers.rs +++ b/contracts/cw721-base/src/helpers.rs @@ -14,19 +14,24 @@ use serde::Serialize; use crate::{ExecuteMsg, QueryMsg}; #[cw_serde] -pub struct Cw721Contract( +pub struct Cw721Contract( pub Addr, - pub PhantomData, - pub PhantomData, + pub PhantomData, + pub PhantomData, ); #[allow(dead_code)] -impl Cw721Contract { +impl + Cw721Contract +{ pub fn addr(&self) -> Addr { self.0.clone() } - pub fn call(&self, msg: ExecuteMsg) -> StdResult { + pub fn call( + &self, + msg: ExecuteMsg, + ) -> StdResult { let msg = to_json_binary(&msg)?; Ok(WasmMsg::Execute { contract_addr: self.addr().into(), @@ -39,7 +44,7 @@ impl Cw721Contract { pub fn query( &self, querier: &QuerierWrapper, - req: QueryMsg, + req: QueryMsg, ) -> StdResult { let query = WasmQuery::Smart { contract_addr: self.addr().into(), diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index 247d22644..df84dd9e6 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -182,7 +182,7 @@ pub mod entry { mod tests { use cosmwasm_std::{ testing::{mock_dependencies, mock_env, mock_info}, - Addr, Order, StdError, StdResult, + Addr, Order, StdResult, }; use cw2::ContractVersion; use cw721::{CollectionInfoResponse, Cw721Query}; diff --git a/contracts/cw721-base/src/msg.rs b/contracts/cw721-base/src/msg.rs index 5fe2f8757..c826de064 100644 --- a/contracts/cw721-base/src/msg.rs +++ b/contracts/cw721-base/src/msg.rs @@ -24,7 +24,7 @@ pub struct InstantiateMsg { /// use other control logic in any contract that inherits this. #[cw_ownable_execute] #[cw_serde] -pub enum ExecuteMsg { +pub enum ExecuteMsg { /// Transfer is a base message to move a token to another account without triggering actions TransferNft { recipient: String, token_id: String }, /// Send is a base message to transfer a token to a contract and trigger an action @@ -63,14 +63,14 @@ pub enum ExecuteMsg { /// Metadata JSON Schema token_uri: Option, /// Any custom extension used by this contract - extension: T, + extension: TMetadata, }, /// Burn an NFT the sender has access to Burn { token_id: String }, /// Extension msg - Extension { msg: E }, + Extension { msg: TExtensionExecuteMsg }, /// Sets address to send withdrawn fees to. Only owner can call this. SetWithdrawAddress { address: String }, @@ -84,7 +84,7 @@ pub enum ExecuteMsg { #[cw_ownable_query] #[cw_serde] #[derive(QueryResponses)] -pub enum QueryMsg { +pub enum QueryMsg { /// Return the owner of the given token, error if token does not exist #[returns(cw721::OwnerOfResponse)] OwnerOf { @@ -136,12 +136,12 @@ pub enum QueryMsg { /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract - #[returns(cw721::NftInfoResponse)] + #[returns(cw721::NftInfoResponse)] NftInfo { token_id: String }, /// With MetaData Extension. /// Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization /// for clients - #[returns(cw721::AllNftInfoResponse)] + #[returns(cw721::AllNftInfoResponse)] AllNftInfo { token_id: String, /// unset or false will filter out expired approvals, you must set to true to see them @@ -170,7 +170,7 @@ pub enum QueryMsg { /// Extension query #[returns(())] - Extension { msg: Q }, + Extension { msg: TMetadataResponse }, #[returns(Option)] GetWithdrawAddress {}, diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index e42942df6..865632e8b 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -19,12 +19,20 @@ use crate::state::{Approval, Cw721Contract, NftInfo}; pub const DEFAULT_LIMIT: u32 = 10; pub const MAX_LIMIT: u32 = 1000; -impl<'a, T, C, E, Q> Cw721Query for Cw721Contract<'a, T, C, E, Q> +impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> + Cw721Query + for Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + > where - T: Serialize + DeserializeOwned + Clone, - C: CustomMsg, - E: CustomMsg, - Q: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, { fn collection_info(&self, deps: Deps) -> StdResult { self.collection_info.load(deps.storage) @@ -35,7 +43,7 @@ where Ok(NumTokensResponse { count }) } - fn nft_info(&self, deps: Deps, token_id: String) -> StdResult> { + fn nft_info(&self, deps: Deps, token_id: String) -> StdResult> { let info = self.nft_info.load(deps.storage, &token_id)?; Ok(NftInfoResponse { token_uri: info.token_uri, @@ -226,7 +234,7 @@ where env: Env, token_id: String, include_expired: bool, - ) -> StdResult> { + ) -> StdResult> { let info = self.nft_info.load(deps.storage, &token_id)?; Ok(AllNftInfoResponse { access: OwnerOfResponse { @@ -241,14 +249,20 @@ where } } -impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> +impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> + Cw721Contract<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> where - T: Serialize + DeserializeOwned + Clone, - C: CustomMsg, - E: CustomMsg, - Q: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, { - pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + pub fn query( + &self, + deps: Deps, + env: Env, + msg: QueryMsg, + ) -> StdResult { match msg { QueryMsg::Minter {} => to_json_binary(&self.minter(deps)?), #[allow(deprecated)] @@ -354,9 +368,9 @@ fn parse_approval(item: StdResult<(Addr, Expiration)>) -> StdResult( +fn humanize_approvals( block: &BlockInfo, - info: &NftInfo, + info: &NftInfo, include_expired: bool, ) -> Vec { info.approvals diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index cc6f5aedc..753ed1ee0 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -8,11 +8,16 @@ use cosmwasm_std::{Addr, BlockInfo, CustomMsg, StdResult, Storage}; use cw721::{CollectionInfoResponse, Cw721, Expiration}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; -pub struct Cw721Contract<'a, T, C, E, Q> -where - T: Serialize + DeserializeOwned + Clone, - Q: CustomMsg, - E: CustomMsg, +pub struct Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, +> where + TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataResponse: CustomMsg, + TExtensionExecuteMsg: CustomMsg, { /// Note: do not use deprecated/legacy key "nft_info"! pub collection_info: Item<'a, CollectionInfoResponse>, @@ -20,29 +25,44 @@ where /// Stored as (granter, operator) giving operator full control over granter's account pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, /// Note: do not use deprecated/legacy keys "tokens" and "tokens__owner"! - pub nft_info: IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, T>>, + pub nft_info: IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, TMetadata>>, pub withdraw_address: Item<'a, String>, - pub(crate) _custom_response: PhantomData, - pub(crate) _custom_query: PhantomData, - pub(crate) _custom_execute: PhantomData, + pub(crate) _custom_response: PhantomData, + pub(crate) _custom_query: PhantomData, + pub(crate) _custom_execute: PhantomData, } // This is a signal, the implementations are in other files -impl<'a, T, C, E, Q> Cw721 for Cw721Contract<'a, T, C, E, Q> +impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> + Cw721 + for Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + > where - T: Serialize + DeserializeOwned + Clone, - C: CustomMsg, - E: CustomMsg, - Q: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, { } -impl Default for Cw721Contract<'static, T, C, E, Q> +impl Default + for Cw721Contract< + 'static, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + > where - T: Serialize + DeserializeOwned + Clone, - E: CustomMsg, - Q: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, { fn default() -> Self { Self::new( @@ -56,11 +76,12 @@ where } } -impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> +impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> + Cw721Contract<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> where - T: Serialize + DeserializeOwned + Clone, - E: CustomMsg, - Q: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, { fn new( collection_info_key: &'a str, @@ -103,10 +124,10 @@ where } #[deprecated(since = "0.19.0", note = "Please use NftInfo")] -pub type TokenInfo = NftInfo; +pub type TokenInfo = NftInfo; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct NftInfo { +pub struct NftInfo { /// The owner of the newly minted NFT pub owner: Addr, /// Approvals are stored here, as we clear them all upon transfer and cannot accumulate much @@ -118,7 +139,7 @@ pub struct NftInfo { pub token_uri: Option, /// You can add any custom metadata here when you extend cw721-base - pub extension: T, + pub extension: TMetadata, } #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] @@ -135,23 +156,23 @@ impl Approval { } } -pub struct TokenIndexes<'a, T> +pub struct TokenIndexes<'a, TMetadata> where - T: Serialize + DeserializeOwned + Clone, + TMetadata: Serialize + DeserializeOwned + Clone, { - pub owner: MultiIndex<'a, Addr, NftInfo, String>, + pub owner: MultiIndex<'a, Addr, NftInfo, String>, } -impl<'a, T> IndexList> for TokenIndexes<'a, T> +impl<'a, TMetadata> IndexList> for TokenIndexes<'a, TMetadata> where - T: Serialize + DeserializeOwned + Clone, + TMetadata: Serialize + DeserializeOwned + Clone, { - fn get_indexes(&'_ self) -> Box>> + '_> { - let v: Vec<&dyn Index>> = vec![&self.owner]; + fn get_indexes(&'_ self) -> Box>> + '_> { + let v: Vec<&dyn Index>> = vec![&self.owner]; Box::new(v.into_iter()) } } -pub fn token_owner_idx(_pk: &[u8], d: &NftInfo) -> Addr { +pub fn token_owner_idx(_pk: &[u8], d: &NftInfo) -> Addr { d.owner.clone() } diff --git a/contracts/cw721-expiration/src/lib.rs b/contracts/cw721-expiration/src/lib.rs index 2fdb4575b..b42a0ee88 100644 --- a/contracts/cw721-expiration/src/lib.rs +++ b/contracts/cw721-expiration/src/lib.rs @@ -14,7 +14,7 @@ const CONTRACT_NAME: &str = "crates.io:cw721-expiration"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub type MinterResponse = cw721_base::msg::MinterResponse; -pub type Extension = Option; +pub use cw721_base::Extension; pub type NftInfo = cw721_base::state::NftInfo; diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index 8e82a7934..d8dc142bc 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -127,21 +127,21 @@ pub struct CollectionInfoResponse { } #[cw_serde] -pub struct NftInfoResponse { +pub struct NftInfoResponse { /// Universal resource identifier for this NFT /// Should point to a JSON file that conforms to the ERC721 /// Metadata JSON Schema pub token_uri: Option, /// You can add any custom metadata here when you extend cw721-base - pub extension: T, + pub extension: TMetadata, } #[cw_serde] -pub struct AllNftInfoResponse { +pub struct AllNftInfoResponse { /// Who can transfer the token pub access: OwnerOfResponse, /// Data on the token itself, - pub info: NftInfoResponse, + pub info: NftInfoResponse, } #[cw_serde] diff --git a/packages/cw721/src/receiver.rs b/packages/cw721/src/receiver.rs index e67097927..6d050b29c 100644 --- a/packages/cw721/src/receiver.rs +++ b/packages/cw721/src/receiver.rs @@ -19,9 +19,12 @@ impl Cw721ReceiveMsg { } /// creates a cosmos_msg sending this struct to the named contract - pub fn into_cosmos_msg, C>(self, contract_addr: T) -> StdResult> + pub fn into_cosmos_msg, TCustomResponseMessage>( + self, + contract_addr: TAddress, + ) -> StdResult> where - C: Clone + std::fmt::Debug + PartialEq + JsonSchema, + TCustomResponseMessage: Clone + std::fmt::Debug + PartialEq + JsonSchema, { let msg = self.into_json_binary()?; let execute = WasmMsg::Execute { diff --git a/packages/cw721/src/traits.rs b/packages/cw721/src/traits.rs index 33d33610c..663bdf3a7 100644 --- a/packages/cw721/src/traits.rs +++ b/packages/cw721/src/traits.rs @@ -9,17 +9,18 @@ use crate::{ use cosmwasm_std::{Binary, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; use cw_utils::Expiration; -pub trait Cw721: Cw721Execute + Cw721Query +pub trait Cw721: + Cw721Execute + Cw721Query where - T: Serialize + DeserializeOwned + Clone, - C: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, { } -pub trait Cw721Execute +pub trait Cw721Execute where - T: Serialize + DeserializeOwned + Clone, - C: CustomMsg, + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, { type Err: ToString; @@ -30,7 +31,7 @@ where info: MessageInfo, recipient: String, token_id: String, - ) -> Result, Self::Err>; + ) -> Result, Self::Err>; fn send_nft( &self, @@ -40,7 +41,7 @@ where contract: String, token_id: String, msg: Binary, - ) -> Result, Self::Err>; + ) -> Result, Self::Err>; fn approve( &self, @@ -50,7 +51,7 @@ where spender: String, token_id: String, expires: Option, - ) -> Result, Self::Err>; + ) -> Result, Self::Err>; fn revoke( &self, @@ -59,7 +60,7 @@ where info: MessageInfo, spender: String, token_id: String, - ) -> Result, Self::Err>; + ) -> Result, Self::Err>; fn approve_all( &self, @@ -68,7 +69,7 @@ where info: MessageInfo, operator: String, expires: Option, - ) -> Result, Self::Err>; + ) -> Result, Self::Err>; fn revoke_all( &self, @@ -76,7 +77,7 @@ where env: Env, info: MessageInfo, operator: String, - ) -> Result, Self::Err>; + ) -> Result, Self::Err>; fn burn( &self, @@ -84,12 +85,12 @@ where env: Env, info: MessageInfo, token_id: String, - ) -> Result, Self::Err>; + ) -> Result, Self::Err>; } -pub trait Cw721Query +pub trait Cw721Query where - T: Serialize + DeserializeOwned + Clone, + TMetadata: Serialize + DeserializeOwned + Clone, { // TODO: use custom error? // How to handle the two derived error types? @@ -98,7 +99,7 @@ where fn num_tokens(&self, deps: Deps) -> StdResult; - fn nft_info(&self, deps: Deps, token_id: String) -> StdResult>; + fn nft_info(&self, deps: Deps, token_id: String) -> StdResult>; fn owner_of( &self, @@ -165,5 +166,5 @@ where env: Env, token_id: String, include_expired: bool, - ) -> StdResult>; + ) -> StdResult>; } From d46d2b7f3e487027fd3a2700a6c8e9523d37f8a2 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 29 Feb 2024 15:05:40 +0100 Subject: [PATCH 07/47] CollectionInfoResponse -> CollectionInfo --- contracts/cw721-base/src/contract_tests.rs | 4 ++-- contracts/cw721-base/src/execute.rs | 4 ++-- contracts/cw721-base/src/helpers.rs | 6 +++--- contracts/cw721-base/src/lib.rs | 11 +++++------ contracts/cw721-base/src/msg.rs | 4 ++-- contracts/cw721-base/src/query.rs | 4 ++-- contracts/cw721-base/src/state.rs | 4 ++-- contracts/cw721-expiration/src/contract_tests.rs | 6 +++--- contracts/cw721-expiration/src/msg.rs | 4 ++-- contracts/cw721-expiration/src/query.rs | 6 +++--- contracts/cw721-non-transferable/examples/schema.rs | 8 ++++---- packages/cw721/examples/schema.rs | 8 ++++---- packages/cw721/src/lib.rs | 8 +++++--- packages/cw721/src/query.rs | 10 +++------- packages/cw721/src/state.rs | 7 +++++++ packages/cw721/src/traits.rs | 7 +++---- 16 files changed, 52 insertions(+), 49 deletions(-) create mode 100644 packages/cw721/src/state.rs diff --git a/contracts/cw721-base/src/contract_tests.rs b/contracts/cw721-base/src/contract_tests.rs index 551b70a75..0fb8bb80f 100644 --- a/contracts/cw721-base/src/contract_tests.rs +++ b/contracts/cw721-base/src/contract_tests.rs @@ -6,7 +6,7 @@ use cosmwasm_std::{ }; use cw721::{ - Approval, ApprovalResponse, CollectionInfoResponse, Cw721Query, Cw721ReceiveMsg, Expiration, + Approval, ApprovalResponse, CollectionInfo, Cw721Query, Cw721ReceiveMsg, Expiration, NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, }; use cw_ownable::OwnershipError; @@ -58,7 +58,7 @@ fn proper_instantiation() { let info = contract.collection_info(deps.as_ref()).unwrap(); assert_eq!( info, - CollectionInfoResponse { + CollectionInfo { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), } diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 3f0486bae..4d1061533 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -6,7 +6,7 @@ use cosmwasm_std::{ Addr, BankMsg, Binary, Coin, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, Storage, }; -use cw721::{CollectionInfoResponse, Cw721Execute, Cw721ReceiveMsg, Expiration}; +use cw721::{CollectionInfo, Cw721Execute, Cw721ReceiveMsg, Expiration}; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg}; @@ -27,7 +27,7 @@ where info: MessageInfo, msg: InstantiateMsg, ) -> Result, ContractError> { - let contract_info = CollectionInfoResponse { + let contract_info = CollectionInfo { name: msg.name, symbol: msg.symbol, }; diff --git a/contracts/cw721-base/src/helpers.rs b/contracts/cw721-base/src/helpers.rs index 5aae2dc8d..156635e46 100644 --- a/contracts/cw721-base/src/helpers.rs +++ b/contracts/cw721-base/src/helpers.rs @@ -5,7 +5,7 @@ use cosmwasm_std::{ to_json_binary, Addr, CosmosMsg, CustomMsg, QuerierWrapper, StdResult, WasmMsg, WasmQuery, }; use cw721::{ - AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, + AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, CollectionInfo, NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; use serde::de::DeserializeOwned; @@ -124,12 +124,12 @@ impl } #[deprecated(since = "0.19.0", note = "Please use collection_info instead")] - pub fn contract_info(&self, querier: &QuerierWrapper) -> StdResult { + pub fn contract_info(&self, querier: &QuerierWrapper) -> StdResult { self.collection_info(querier) } /// With metadata extension - pub fn collection_info(&self, querier: &QuerierWrapper) -> StdResult { + pub fn collection_info(&self, querier: &QuerierWrapper) -> StdResult { let req = QueryMsg::CollectionInfo {}; self.query(querier, req) } diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index df84dd9e6..abfb828e1 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -41,7 +41,7 @@ pub mod entry { use cosmwasm_std::{ Addr, Api, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult, Storage, }; - use cw721::CollectionInfoResponse; + use cw721::CollectionInfo; use cw_ownable::OWNERSHIP; use cw_storage_plus::{IndexedMap, Item, MultiIndex}; @@ -133,8 +133,7 @@ pub mod entry { match contract.collection_info.may_load(storage)? { Some(_) => Ok(response), None => { - let legacy_collection_info_store: Item = - Item::new("nft_info"); + let legacy_collection_info_store: Item = Item::new("nft_info"); let legacy_collection_info = legacy_collection_info_store.load(storage)?; contract .collection_info @@ -185,7 +184,7 @@ mod tests { Addr, Order, StdResult, }; use cw2::ContractVersion; - use cw721::{CollectionInfoResponse, Cw721Query}; + use cw721::{CollectionInfo, Cw721Query}; use cw_storage_plus::{IndexedMap, Item, MultiIndex}; use crate::{ @@ -305,7 +304,7 @@ mod tests { let legacy_minter = legacy_minter_store.load(deps.as_ref().storage).unwrap(); assert_eq!(legacy_minter, "legacy_minter"); // - legacy collection info is set - let legacy_collection_info_store: Item = Item::new("nft_info"); + let legacy_collection_info_store: Item = Item::new("nft_info"); let legacy_collection_info = legacy_collection_info_store .load(deps.as_ref().storage) .unwrap(); @@ -347,7 +346,7 @@ mod tests { // assert collection info let collection_info = contract.collection_info(deps.as_ref()).unwrap(); - let legacy_contract_info = CollectionInfoResponse { + let legacy_contract_info = CollectionInfo { name: "legacy_name".to_string(), symbol: "legacy_symbol".to_string(), }; diff --git a/contracts/cw721-base/src/msg.rs b/contracts/cw721-base/src/msg.rs index c826de064..bf4215f22 100644 --- a/contracts/cw721-base/src/msg.rs +++ b/contracts/cw721-base/src/msg.rs @@ -126,12 +126,12 @@ pub enum QueryMsg { NumTokens {}, #[deprecated(since = "0.19.0", note = "Please use CollectionInfo instead")] - #[returns(cw721::CollectionInfoResponse)] + #[returns(cw721::CollectionInfo)] ContractInfo {}, /// With MetaData Extension. /// Returns top-level metadata about the contract - #[returns(cw721::CollectionInfoResponse)] + #[returns(cw721::CollectionInfo)] CollectionInfo {}, /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index 865632e8b..cc6dc65e5 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -6,7 +6,7 @@ use cosmwasm_std::{ }; use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, Cw721Query, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721Query, Expiration, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; @@ -34,7 +34,7 @@ where TExtensionExecuteMsg: CustomMsg, TMetadataResponse: CustomMsg, { - fn collection_info(&self, deps: Deps) -> StdResult { + fn collection_info(&self, deps: Deps) -> StdResult { self.collection_info.load(deps.storage) } diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 753ed1ee0..82307f621 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -5,7 +5,7 @@ use std::marker::PhantomData; use cosmwasm_std::{Addr, BlockInfo, CustomMsg, StdResult, Storage}; -use cw721::{CollectionInfoResponse, Cw721, Expiration}; +use cw721::{CollectionInfo, Cw721, Expiration}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; pub struct Cw721Contract< @@ -20,7 +20,7 @@ pub struct Cw721Contract< TExtensionExecuteMsg: CustomMsg, { /// Note: do not use deprecated/legacy key "nft_info"! - pub collection_info: Item<'a, CollectionInfoResponse>, + pub collection_info: Item<'a, CollectionInfo>, pub token_count: Item<'a, u64>, /// Stored as (granter, operator) giving operator full control over granter's account pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, diff --git a/contracts/cw721-expiration/src/contract_tests.rs b/contracts/cw721-expiration/src/contract_tests.rs index 65b7087dd..a0f0cdb12 100644 --- a/contracts/cw721-expiration/src/contract_tests.rs +++ b/contracts/cw721-expiration/src/contract_tests.rs @@ -7,8 +7,8 @@ use cosmwasm_std::{ }; use cw721::{ - Approval, ApprovalResponse, CollectionInfoResponse, Cw721ReceiveMsg, Expiration, - NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + Approval, ApprovalResponse, CollectionInfo, Cw721ReceiveMsg, Expiration, NftInfoResponse, + OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; use cw_ownable::OwnershipError; @@ -64,7 +64,7 @@ fn proper_instantiation() { let info = contract.contract_info(deps.as_ref()).unwrap(); assert_eq!( info, - CollectionInfoResponse { + CollectionInfo { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), } diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index b0f5da375..ef1f7f707 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -74,12 +74,12 @@ pub enum QueryMsg { NumTokens {}, #[deprecated(since = "0.19.0", note = "Please use CollectionInfo instead")] - #[returns(cw721::CollectionInfoResponse)] + #[returns(cw721::CollectionInfo)] ContractInfo {}, /// With MetaData Extension. /// Returns top-level metadata about the contract - #[returns(cw721::CollectionInfoResponse)] + #[returns(cw721::CollectionInfo)] CollectionInfo {}, /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index 2eb6a60c5..457975122 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{to_json_binary, Addr, Binary, Deps, Env, StdResult}; use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, Cw721Query, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721Query, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; @@ -135,11 +135,11 @@ impl<'a> Cw721ExpirationContract<'a> { // queries impl<'a> Cw721ExpirationContract<'a> { - pub fn contract_info(&self, deps: Deps) -> StdResult { + pub fn contract_info(&self, deps: Deps) -> StdResult { self.base_contract.collection_info(deps) } - pub fn collection_info(&self, deps: Deps) -> StdResult { + pub fn collection_info(&self, deps: Deps) -> StdResult { self.base_contract.collection_info.load(deps.storage) } diff --git a/contracts/cw721-non-transferable/examples/schema.rs b/contracts/cw721-non-transferable/examples/schema.rs index 9cd8ed434..f4239e3cf 100644 --- a/contracts/cw721-non-transferable/examples/schema.rs +++ b/contracts/cw721-non-transferable/examples/schema.rs @@ -5,9 +5,9 @@ use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, s #[allow(deprecated)] use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, - ContractInfoResponse, Cw721ExecuteMsg, NftInfoResponse, NumTokensResponse, OperatorsResponse, - OwnerOfResponse, TokensResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, ContractInfoResponse, + Cw721ExecuteMsg, NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, + TokensResponse, }; use cw721_non_transferable::{Extension, InstantiateMsg, MinterResponse, QueryMsg}; @@ -30,7 +30,7 @@ fn main() { export_schema(&schema_for!(OperatorsResponse), &out_dir); #[allow(deprecated)] export_schema(&schema_for!(ContractInfoResponse), &out_dir); - export_schema(&schema_for!(CollectionInfoResponse), &out_dir); + export_schema(&schema_for!(CollectionInfo), &out_dir); export_schema(&schema_for!(MinterResponse), &out_dir); export_schema_with_title( &schema_for!(NftInfoResponse), diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 5255b1f4a..88babd7f6 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -6,9 +6,9 @@ use cosmwasm_std::Empty; #[allow(deprecated)] use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, - ContractInfoResponse, Cw721ExecuteMsg, Cw721QueryMsg, Cw721ReceiveMsg, NftInfoResponse, - NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, ContractInfoResponse, + Cw721ExecuteMsg, Cw721QueryMsg, Cw721ReceiveMsg, NftInfoResponse, NumTokensResponse, + OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; type Extension = Option; @@ -33,7 +33,7 @@ fn main() { export_schema(&schema_for!(OperatorsResponse), &out_dir); #[allow(deprecated)] export_schema(&schema_for!(ContractInfoResponse), &out_dir); - export_schema(&schema_for!(CollectionInfoResponse), &out_dir); + export_schema(&schema_for!(CollectionInfo), &out_dir); export_schema(&schema_for!(OwnerOfResponse), &out_dir); export_schema_with_title( &schema_for!(NftInfoResponse), diff --git a/packages/cw721/src/lib.rs b/packages/cw721/src/lib.rs index dd76ef9f5..cef9fbd73 100644 --- a/packages/cw721/src/lib.rs +++ b/packages/cw721/src/lib.rs @@ -1,6 +1,7 @@ mod msg; mod query; mod receiver; +mod state; mod traits; pub use cw_utils::Expiration; @@ -8,9 +9,10 @@ pub use cw_utils::Expiration; pub use crate::msg::Cw721ExecuteMsg; #[allow(deprecated)] pub use crate::query::{ - AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, - ContractInfoResponse, Cw721QueryMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, - OperatorsResponse, OwnerOfResponse, TokensResponse, + AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, + Cw721QueryMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, + OwnerOfResponse, TokensResponse, }; pub use crate::receiver::Cw721ReceiveMsg; +pub use crate::state::CollectionInfo; pub use crate::traits::{Cw721, Cw721Execute, Cw721Query}; diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index d8dc142bc..8c7f1a228 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -1,6 +1,8 @@ use cosmwasm_schema::cw_serde; use cw_utils::Expiration; +use crate::CollectionInfo; + #[cw_serde] pub enum Cw721QueryMsg { /// Return the owner of the given token, error if token does not exist @@ -118,13 +120,7 @@ pub struct NumTokensResponse { #[deprecated(since = "0.19.0", note = "Please use CollectionInfoResponse instead")] #[allow(dead_code)] -pub type ContractInfoResponse = CollectionInfoResponse; - -#[cw_serde] -pub struct CollectionInfoResponse { - pub name: String, - pub symbol: String, -} +pub type ContractInfoResponse = CollectionInfo; #[cw_serde] pub struct NftInfoResponse { diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs new file mode 100644 index 000000000..00ab4b498 --- /dev/null +++ b/packages/cw721/src/state.rs @@ -0,0 +1,7 @@ +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct CollectionInfo { + pub name: String, + pub symbol: String, +} diff --git a/packages/cw721/src/traits.rs b/packages/cw721/src/traits.rs index 663bdf3a7..6cd6ec332 100644 --- a/packages/cw721/src/traits.rs +++ b/packages/cw721/src/traits.rs @@ -2,9 +2,8 @@ use serde::de::DeserializeOwned; use serde::Serialize; use crate::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoResponse, - NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, - TokensResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, NftInfoResponse, + NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; use cosmwasm_std::{Binary, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; use cw_utils::Expiration; @@ -95,7 +94,7 @@ where // TODO: use custom error? // How to handle the two derived error types? - fn collection_info(&self, deps: Deps) -> StdResult; + fn collection_info(&self, deps: Deps) -> StdResult; fn num_tokens(&self, deps: Deps) -> StdResult; From 3d101cb459d058cb29e52127e4abd1bdb86df032 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 29 Feb 2024 22:10:55 +0100 Subject: [PATCH 08/47] add CollectionInfoExtension with RoyaltyInfo --- Cargo.lock | 1 + Cargo.toml | 1 + contracts/cw2981-royalties/examples/schema.rs | 3 +- contracts/cw2981-royalties/src/lib.rs | 14 ++- contracts/cw721-base/Cargo.toml | 1 + contracts/cw721-base/examples/schema.rs | 4 +- contracts/cw721-base/src/contract_tests.rs | 23 +++-- contracts/cw721-base/src/execute.rs | 96 +++++++++++++++---- contracts/cw721-base/src/helpers.rs | 26 +++-- contracts/cw721-base/src/lib.rs | 92 ++++++++++++------ contracts/cw721-base/src/msg.rs | 13 ++- contracts/cw721-base/src/query.rs | 33 ++++++- contracts/cw721-base/src/state.rs | 48 ++++++++-- contracts/cw721-expiration/examples/schema.rs | 3 +- .../cw721-expiration/src/contract_tests.rs | 16 +++- contracts/cw721-expiration/src/execute.rs | 22 +++-- contracts/cw721-expiration/src/lib.rs | 18 ++-- contracts/cw721-expiration/src/msg.rs | 19 ++-- contracts/cw721-expiration/src/query.rs | 20 ++-- contracts/cw721-expiration/src/state.rs | 11 ++- .../cw721-fixed-price/examples/schema.rs | 3 +- contracts/cw721-fixed-price/src/contract.rs | 24 ++++- contracts/cw721-fixed-price/src/msg.rs | 9 +- contracts/cw721-fixed-price/src/state.rs | 4 +- .../cw721-metadata-onchain/examples/schema.rs | 3 +- contracts/cw721-metadata-onchain/src/lib.rs | 57 +++++------ .../cw721-non-transferable/examples/schema.rs | 17 +++- contracts/cw721-non-transferable/src/lib.rs | 13 ++- contracts/cw721-non-transferable/src/msg.rs | 4 +- .../cw721-receiver-tester/tests/multitest.rs | 4 +- packages/cw721/examples/schema.rs | 14 +-- packages/cw721/src/lib.rs | 7 +- packages/cw721/src/query.rs | 4 +- packages/cw721/src/state.rs | 44 ++++++++- packages/cw721/src/traits.rs | 10 +- 35 files changed, 493 insertions(+), 188 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2eba40509..282e67556 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -415,6 +415,7 @@ dependencies = [ "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", + "cw721 0.16.0", "cw721 0.19.0", "cw721-base 0.16.0", "schemars", diff --git a/Cargo.toml b/Cargo.toml index c422d777c..dfca3e6d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ cosmwasm-std = "^1.2" cw2 = "^1.1" cw20 = "^1.1" cw721 = { version = "*", path = "./packages/cw721" } +cw721-016 = { version = "0.16.0", package = "cw721" } cw721-base = { version = "*", path = "./contracts/cw721-base" } cw721-base-016 = { version = "0.16.0", package = "cw721-base" } cw-multi-test = "^0.20" diff --git a/contracts/cw2981-royalties/examples/schema.rs b/contracts/cw2981-royalties/examples/schema.rs index f3da971f8..3ff748de6 100644 --- a/contracts/cw2981-royalties/examples/schema.rs +++ b/contracts/cw2981-royalties/examples/schema.rs @@ -1,10 +1,11 @@ use cosmwasm_schema::write_api; use cw2981_royalties::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cw721::EmptyCollectionInfoExtension; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: InstantiateMsg, execute: ExecuteMsg, query: QueryMsg, } diff --git a/contracts/cw2981-royalties/src/lib.rs b/contracts/cw2981-royalties/src/lib.rs index 194412495..f5c72d139 100644 --- a/contracts/cw2981-royalties/src/lib.rs +++ b/contracts/cw2981-royalties/src/lib.rs @@ -2,6 +2,7 @@ pub mod error; pub mod msg; pub mod query; +use cw721::EmptyCollectionInfoExtension; pub use query::{check_royalties, query_royalties_info}; use cosmwasm_schema::cw_serde; @@ -49,7 +50,8 @@ pub type Extension = Option; pub type MintExtension = Option; -pub type Cw2981Contract<'a> = Cw721Contract<'a, Extension, Empty, Empty, Cw2981QueryMsg>; +pub type Cw2981Contract<'a> = + Cw721Contract<'a, Extension, Empty, Empty, Cw2981QueryMsg, EmptyCollectionInfoExtension>; pub type ExecuteMsg = cw721_base::ExecuteMsg; pub type QueryMsg = cw721_base::QueryMsg; @@ -65,7 +67,7 @@ pub mod entry { mut deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -136,7 +138,9 @@ mod tests { let init_msg = InstantiateMsg { name: "SpaceShips".to_string(), symbol: "SPACE".to_string(), + collection_info_extension: None, minter: None, + creator: None, withdraw_address: None, }; entry::instantiate(deps.as_mut(), mock_env(), info.clone(), init_msg).unwrap(); @@ -170,7 +174,9 @@ mod tests { let init_msg = InstantiateMsg { name: "SpaceShips".to_string(), symbol: "SPACE".to_string(), + collection_info_extension: None, minter: None, + creator: None, withdraw_address: None, }; entry::instantiate(deps.as_mut(), mock_env(), info.clone(), init_msg).unwrap(); @@ -201,7 +207,9 @@ mod tests { let init_msg = InstantiateMsg { name: "SpaceShips".to_string(), symbol: "SPACE".to_string(), + collection_info_extension: None, minter: None, + creator: None, withdraw_address: None, }; entry::instantiate(deps.as_mut(), mock_env(), info.clone(), init_msg).unwrap(); @@ -242,7 +250,9 @@ mod tests { let init_msg = InstantiateMsg { name: "SpaceShips".to_string(), symbol: "SPACE".to_string(), + collection_info_extension: None, minter: None, + creator: None, withdraw_address: None, }; entry::instantiate(deps.as_mut(), mock_env(), info.clone(), init_msg).unwrap(); diff --git a/contracts/cw721-base/Cargo.toml b/contracts/cw721-base/Cargo.toml index 3b140ce27..0058d7d6a 100644 --- a/contracts/cw721-base/Cargo.toml +++ b/contracts/cw721-base/Cargo.toml @@ -31,6 +31,7 @@ cw-storage-plus = { workspace = true } cw-utils = { workspace = true } cw2 = { workspace = true } cw721 = { workspace = true } +cw721-016 = { workspace = true } cw721-base-016 = { workspace = true, features = ["library"] } schemars = { workspace = true } serde = { workspace = true } diff --git a/contracts/cw721-base/examples/schema.rs b/contracts/cw721-base/examples/schema.rs index 390811894..13736af0a 100644 --- a/contracts/cw721-base/examples/schema.rs +++ b/contracts/cw721-base/examples/schema.rs @@ -1,11 +1,11 @@ use cosmwasm_schema::write_api; use cosmwasm_std::Empty; -use cw721_base::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cw721_base::{EmptyCollectionInfoExtension, ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: InstantiateMsg, execute: ExecuteMsg, query: QueryMsg, } diff --git a/contracts/cw721-base/src/contract_tests.rs b/contracts/cw721-base/src/contract_tests.rs index 0fb8bb80f..1ce32b55f 100644 --- a/contracts/cw721-base/src/contract_tests.rs +++ b/contracts/cw721-base/src/contract_tests.rs @@ -12,19 +12,25 @@ use cw721::{ use cw_ownable::OwnershipError; use crate::{ - ContractError, Cw721Contract, ExecuteMsg, Extension, InstantiateMsg, MinterResponse, QueryMsg, + ContractError, Cw721Contract, EmptyExtension, ExecuteMsg, InstantiateMsg, MinterResponse, + QueryMsg, }; -const MINTER: &str = "merlin"; +const MINTER: &str = "minter"; +const CREATOR: &str = "creator"; const CONTRACT_NAME: &str = "Magic Power"; const SYMBOL: &str = "MGK"; -fn setup_contract(deps: DepsMut<'_>) -> Cw721Contract<'static, Extension, Empty, Empty, Empty> { +fn setup_contract( + deps: DepsMut<'_>, +) -> Cw721Contract<'static, EmptyExtension, Empty, Empty, Empty, Empty> { let contract = Cw721Contract::default(); let msg = InstantiateMsg { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), + collection_info_extension: Empty {}, minter: Some(String::from(MINTER)), + creator: Some(String::from(CREATOR)), withdraw_address: None, }; let info = mock_info("creator", &[]); @@ -36,19 +42,22 @@ fn setup_contract(deps: DepsMut<'_>) -> Cw721Contract<'static, Extension, Empty, #[test] fn proper_instantiation() { let mut deps = mock_dependencies(); - let contract = Cw721Contract::::default(); + let contract = Cw721Contract::::default(); let msg = InstantiateMsg { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), + collection_info_extension: Empty {}, minter: Some(String::from(MINTER)), + creator: Some(String::from(CREATOR)), withdraw_address: Some(String::from(MINTER)), }; let info = mock_info("creator", &[]); + let env = mock_env(); // we can just call .unwrap() to assert this was a success let res = contract - .instantiate(deps.as_mut(), mock_env(), info, msg) + .instantiate(deps.as_mut(), env.clone(), info, msg) .unwrap(); assert_eq!(0, res.messages.len()); @@ -61,6 +70,8 @@ fn proper_instantiation() { CollectionInfo { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), + extension: Empty {}, + updated_at: env.block.time } ); @@ -119,7 +130,7 @@ fn minting() { let info = contract.nft_info(deps.as_ref(), token_id.clone()).unwrap(); assert_eq!( info, - NftInfoResponse:: { + NftInfoResponse:: { token_uri: Some(token_uri), extension: None, } diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 4d1061533..fe3d88140 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -10,40 +10,64 @@ use cw721::{CollectionInfo, Cw721Execute, Cw721ReceiveMsg, Expiration}; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg}; -use crate::state::{Approval, Cw721Contract, NftInfo}; +use crate::state::{Approval, Cw721Contract, NftInfo, CREATOR}; -impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> - Cw721Contract<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > + Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > where TMetadata: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub fn instantiate( &self, deps: DepsMut, - _env: Env, + env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result, ContractError> { - let contract_info = CollectionInfo { + let collection_info = CollectionInfo { name: msg.name, symbol: msg.symbol, + extension: msg.collection_info_extension, + updated_at: env.block.time, }; - self.collection_info.save(deps.storage, &contract_info)?; + self.collection_info.save(deps.storage, &collection_info)?; - let owner = match msg.minter { + let minter = match msg.minter { + Some(owner) => deps.api.addr_validate(&owner)?, + None => info.sender.clone(), + }; + cw_ownable::initialize_owner(deps.storage, deps.api, Some(minter.as_ref()))?; + let creator = match msg.creator { Some(owner) => deps.api.addr_validate(&owner)?, None => info.sender, }; - cw_ownable::initialize_owner(deps.storage, deps.api, Some(owner.as_ref()))?; + CREATOR.initialize_owner(deps.storage, deps.api, Some(creator.as_ref()))?; if let Some(address) = msg.withdraw_address { - self.set_withdraw_address(deps, &owner, address)?; + self.set_withdraw_address(deps, &minter, address)?; } - Ok(Response::default()) + Ok(Response::default() + .add_attribute("minter", minter) + .add_attribute("creator", creator)) } pub fn execute( @@ -96,13 +120,28 @@ where } // TODO pull this into some sort of trait extension?? -impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> - Cw721Contract<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > + Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > where TMetadata: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub fn mint( &self, @@ -202,20 +241,28 @@ where } } -impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> - Cw721Execute +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > Cw721Execute for Cw721Contract< 'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse, + TCollectionInfoExtension, > where TMetadata: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { type Err = ContractError; @@ -361,13 +408,28 @@ where } // helpers -impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> - Cw721Contract<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > + Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > where TMetadata: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub fn _transfer_nft( &self, diff --git a/contracts/cw721-base/src/helpers.rs b/contracts/cw721-base/src/helpers.rs index 156635e46..2bfa8877e 100644 --- a/contracts/cw721-base/src/helpers.rs +++ b/contracts/cw721-base/src/helpers.rs @@ -2,7 +2,8 @@ use std::marker::PhantomData; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - to_json_binary, Addr, CosmosMsg, CustomMsg, QuerierWrapper, StdResult, WasmMsg, WasmQuery, + to_json_binary, Addr, CosmosMsg, CustomMsg, Empty, QuerierWrapper, StdResult, WasmMsg, + WasmQuery, }; use cw721::{ AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, CollectionInfo, @@ -14,15 +15,20 @@ use serde::Serialize; use crate::{ExecuteMsg, QueryMsg}; #[cw_serde] -pub struct Cw721Contract( +pub struct Cw721Contract< + TMetadataResponse: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TCollectionInfoExtension, +>( pub Addr, pub PhantomData, pub PhantomData, + pub PhantomData, ); #[allow(dead_code)] -impl - Cw721Contract +impl + Cw721Contract { pub fn addr(&self) -> Addr { self.0.clone() @@ -124,12 +130,18 @@ impl } #[deprecated(since = "0.19.0", note = "Please use collection_info instead")] - pub fn contract_info(&self, querier: &QuerierWrapper) -> StdResult { + pub fn contract_info( + &self, + querier: &QuerierWrapper, + ) -> StdResult> { self.collection_info(querier) } /// With metadata extension - pub fn collection_info(&self, querier: &QuerierWrapper) -> StdResult { + pub fn collection_info( + &self, + querier: &QuerierWrapper, + ) -> StdResult> { let req = QueryMsg::CollectionInfo {}; self.query(querier, req) } @@ -189,7 +201,7 @@ impl /// returns true if the contract supports the metadata extension pub fn has_metadata(&self, querier: &QuerierWrapper) -> bool { - self.collection_info(querier).is_ok() + self.collection_info::(querier).is_ok() } /// returns true if the contract supports the enumerable extension diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index abfb828e1..e4efc59f0 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -24,8 +24,9 @@ pub use cw_ownable::{Action, Ownership, OwnershipError}; use cosmwasm_std::Empty; -// This is a simple type to let us handle empty extensions -pub type Extension = Option; +// These are simple type to let us handle empty extensions +pub use cw721::EmptyCollectionInfoExtension; +pub use cw721::EmptyExtension; // Version info for migration pub const CONTRACT_NAME: &str = "crates.io:cw721-base"; @@ -51,11 +52,17 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - let tract = Cw721Contract::::default(); + let tract = Cw721Contract::< + EmptyExtension, + Empty, + Empty, + Empty, + EmptyCollectionInfoExtension, + >::default(); tract.instantiate(deps, env, info, msg) } @@ -64,15 +71,15 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: ExecuteMsg, + msg: ExecuteMsg, ) -> Result { - let tract = Cw721Contract::::default(); + let tract = Cw721Contract::::default(); tract.execute(deps, env, info, msg) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { - let tract = Cw721Contract::::default(); + let tract = Cw721Contract::::default(); tract.query(deps, env, msg) } @@ -125,19 +132,30 @@ pub mod entry { /// Migrates only in case collection_info is not present pub fn migrate_legacy_contract_info( storage: &mut dyn Storage, - _env: &Env, + env: &Env, _msg: &Empty, response: Response, ) -> Result { - let contract = Cw721Contract::::default(); + let contract = Cw721Contract::< + EmptyExtension, + Empty, + Empty, + Empty, + EmptyCollectionInfoExtension, + >::default(); match contract.collection_info.may_load(storage)? { Some(_) => Ok(response), None => { - let legacy_collection_info_store: Item = Item::new("nft_info"); + let legacy_collection_info_store: Item = + Item::new("nft_info"); let legacy_collection_info = legacy_collection_info_store.load(storage)?; - contract - .collection_info - .save(storage, &legacy_collection_info)?; + let collection_info = CollectionInfo { + name: legacy_collection_info.name.clone(), + symbol: legacy_collection_info.symbol.clone(), + extension: None, + updated_at: env.block.time, + }; + contract.collection_info.save(storage, &collection_info)?; Ok(response .add_attribute("migrated collection name", legacy_collection_info.name) .add_attribute("migrated collection symbol", legacy_collection_info.symbol)) @@ -152,7 +170,7 @@ pub mod entry { _msg: &Empty, response: Response, ) -> Result { - let contract = Cw721Contract::::default(); + let contract = Cw721Contract::::default(); match contract.nft_info.is_empty(storage) { false => Ok(response), true => { @@ -161,8 +179,8 @@ pub mod entry { }; let legacy_tokens_store: IndexedMap< &str, - NftInfo, - TokenIndexes, + NftInfo, + TokenIndexes, > = IndexedMap::new("tokens", indexes); let keys = legacy_tokens_store .keys(storage, None, None, Order::Ascending) @@ -189,7 +207,7 @@ mod tests { use crate::{ query::MAX_LIMIT, - state::{token_owner_idx, NftInfo, TokenIndexes}, + state::{token_owner_idx, NftInfo, TokenIndexes, CREATOR}, }; use super::*; @@ -204,9 +222,11 @@ mod tests { mock_env(), mock_info("larry", &[]), InstantiateMsg { - name: "".into(), - symbol: "".into(), - minter: Some("other".into()), + name: "collection_name".into(), + symbol: "collection_symbol".into(), + collection_info_extension: None, + minter: Some("minter".into()), + creator: Some("creator".into()), withdraw_address: None, }, ) @@ -216,7 +236,7 @@ mod tests { .unwrap() .owner .map(|a| a.into_string()); - assert_eq!(minter, Some("other".to_string())); + assert_eq!(minter, Some("minter".to_string())); let version = cw2::get_contract_version(deps.as_ref().storage).unwrap(); assert_eq!( @@ -237,8 +257,10 @@ mod tests { mock_env(), mock_info("owner", &[]), InstantiateMsg { - name: "".into(), - symbol: "".into(), + name: "collection_name".into(), + symbol: "collection_symbol".into(), + collection_info_extension: None, + creator: None, minter: None, withdraw_address: None, }, @@ -250,6 +272,8 @@ mod tests { .owner .map(|a| a.into_string()); assert_eq!(minter, Some("owner".to_string())); + let creator = CREATOR.item.load(deps.as_ref().storage).unwrap().owner; + assert_eq!(creator, Some(Addr::unchecked("owner"))); } #[test] @@ -285,7 +309,13 @@ mod tests { // assert new data before migration: // - ownership and collection info throws NotFound Error cw_ownable::get_ownership(deps.as_ref().storage).unwrap_err(); - let contract = Cw721Contract::::default(); + let contract = Cw721Contract::< + EmptyExtension, + Empty, + Empty, + Empty, + EmptyCollectionInfoExtension, + >::default(); contract.collection_info(deps.as_ref()).unwrap_err(); // - no tokens let all_tokens = contract @@ -304,7 +334,8 @@ mod tests { let legacy_minter = legacy_minter_store.load(deps.as_ref().storage).unwrap(); assert_eq!(legacy_minter, "legacy_minter"); // - legacy collection info is set - let legacy_collection_info_store: Item = Item::new("nft_info"); + let legacy_collection_info_store: Item = + Item::new("nft_info"); let legacy_collection_info = legacy_collection_info_store .load(deps.as_ref().storage) .unwrap(); @@ -314,8 +345,11 @@ mod tests { let indexes = TokenIndexes { owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"), }; - let legacy_tokens_store: IndexedMap<&str, NftInfo, TokenIndexes> = - IndexedMap::new("tokens", indexes); + let legacy_tokens_store: IndexedMap< + &str, + NftInfo, + TokenIndexes, + > = IndexedMap::new("tokens", indexes); let keys = legacy_tokens_store .keys(deps.as_ref().storage, None, None, Order::Ascending) .collect::>>() @@ -328,7 +362,7 @@ mod tests { assert_eq!(legacy_token.owner.as_str(), "owner"); } - entry::migrate(deps.as_mut(), env, Empty {}).unwrap(); + entry::migrate(deps.as_mut(), env.clone(), Empty {}).unwrap(); // version let version = cw2::get_contract_version(deps.as_ref().storage) @@ -349,6 +383,8 @@ mod tests { let legacy_contract_info = CollectionInfo { name: "legacy_name".to_string(), symbol: "legacy_symbol".to_string(), + extension: None, + updated_at: env.block.time, }; assert_eq!(collection_info, legacy_contract_info); diff --git a/contracts/cw721-base/src/msg.rs b/contracts/cw721-base/src/msg.rs index bf4215f22..3b29f030e 100644 --- a/contracts/cw721-base/src/msg.rs +++ b/contracts/cw721-base/src/msg.rs @@ -1,21 +1,26 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Binary, Coin}; +use cosmwasm_std::{Binary, Coin, Empty}; use cw721::Expiration; use cw_ownable::{cw_ownable_execute, cw_ownable_query}; use schemars::JsonSchema; #[cw_serde] -pub struct InstantiateMsg { +pub struct InstantiateMsg { /// Name of the NFT contract pub name: String, /// Symbol of the NFT contract pub symbol: String, + pub collection_info_extension: TCollectionInfoExtension, + /// The minter is the only one who can create new NFTs. /// This is designed for a base NFT that is controlled by an external program /// or contract. You will likely replace this with custom logic in custom NFTs pub minter: Option, + /// The creator is the only who can update collection info. + pub creator: Option, + pub withdraw_address: Option, } @@ -126,12 +131,12 @@ pub enum QueryMsg { NumTokens {}, #[deprecated(since = "0.19.0", note = "Please use CollectionInfo instead")] - #[returns(cw721::CollectionInfo)] + #[returns(cw721::CollectionInfo)] ContractInfo {}, /// With MetaData Extension. /// Returns top-level metadata about the contract - #[returns(cw721::CollectionInfo)] + #[returns(cw721::CollectionInfo)] CollectionInfo {}, /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index cc6dc65e5..86c6cf9d7 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -19,22 +19,30 @@ use crate::state::{Approval, Cw721Contract, NftInfo}; pub const DEFAULT_LIMIT: u32 = 10; pub const MAX_LIMIT: u32 = 1000; -impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> - Cw721Query +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > Cw721Query for Cw721Contract< 'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse, + TCollectionInfoExtension, > where TMetadata: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { - fn collection_info(&self, deps: Deps) -> StdResult { + fn collection_info(&self, deps: Deps) -> StdResult> { self.collection_info.load(deps.storage) } @@ -249,13 +257,28 @@ where } } -impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> - Cw721Contract<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > + Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > where TMetadata: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub fn query( &self, diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 82307f621..a963ba7d6 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -1,3 +1,4 @@ +use cw_ownable::OwnershipStore; use schemars::JsonSchema; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -8,19 +9,23 @@ use cosmwasm_std::{Addr, BlockInfo, CustomMsg, StdResult, Storage}; use cw721::{CollectionInfo, Cw721, Expiration}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; +pub const CREATOR: OwnershipStore = OwnershipStore::new("collection_creator"); + pub struct Cw721Contract< 'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse, + TCollectionInfoExtension, > where TMetadata: Serialize + DeserializeOwned + Clone, TMetadataResponse: CustomMsg, TExtensionExecuteMsg: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { /// Note: do not use deprecated/legacy key "nft_info"! - pub collection_info: Item<'a, CollectionInfo>, + pub collection_info: Item<'a, CollectionInfo>, pub token_count: Item<'a, u64>, /// Stored as (granter, operator) giving operator full control over granter's account pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, @@ -34,35 +39,51 @@ pub struct Cw721Contract< } // This is a signal, the implementations are in other files -impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> - Cw721 +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > Cw721 for Cw721Contract< 'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse, + TCollectionInfoExtension, > where TMetadata: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } -impl Default +impl< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > Default for Cw721Contract< 'static, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse, + TCollectionInfoExtension, > where TMetadata: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { Self::new( @@ -76,12 +97,27 @@ where } } -impl<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> - Cw721Contract<'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse> +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > + Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > where TMetadata: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn new( collection_info_key: &'a str, diff --git a/contracts/cw721-expiration/examples/schema.rs b/contracts/cw721-expiration/examples/schema.rs index aadffcf75..2c5ee717e 100644 --- a/contracts/cw721-expiration/examples/schema.rs +++ b/contracts/cw721-expiration/examples/schema.rs @@ -1,9 +1,10 @@ use cosmwasm_schema::write_api; +use cw721::EmptyCollectionInfoExtension; use cw721_expiration::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: InstantiateMsg, execute: ExecuteMsg, query: QueryMsg, } diff --git a/contracts/cw721-expiration/src/contract_tests.rs b/contracts/cw721-expiration/src/contract_tests.rs index a0f0cdb12..0e585f640 100644 --- a/contracts/cw721-expiration/src/contract_tests.rs +++ b/contracts/cw721-expiration/src/contract_tests.rs @@ -14,12 +14,13 @@ use cw_ownable::OwnershipError; use crate::state::Cw721ExpirationContract; use crate::{ - error::ContractError, msg::ExecuteMsg, msg::InstantiateMsg, msg::QueryMsg, Extension, + error::ContractError, msg::ExecuteMsg, msg::InstantiateMsg, msg::QueryMsg, EmptyExtension, MinterResponse, }; use cw721_base::ContractError as Cw721ContractError; -const MINTER: &str = "merlin"; +const MINTER: &str = "minter"; +const CREATOR: &str = "creator"; const CONTRACT_NAME: &str = "Magic Power"; const SYMBOL: &str = "MGK"; @@ -29,7 +30,9 @@ fn setup_contract(deps: DepsMut<'_>, expiration_days: u16) -> Cw721ExpirationCon expiration_days, name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), + extension: None, minter: Some(String::from(MINTER)), + creator: Some(String::from(CREATOR)), withdraw_address: None, }; let info = mock_info("creator", &[]); @@ -47,14 +50,17 @@ fn proper_instantiation() { expiration_days: 1, name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), + extension: None, minter: Some(String::from(MINTER)), + creator: Some(String::from(CREATOR)), withdraw_address: None, }; let info = mock_info("creator", &[]); + let env = mock_env(); // we can just call .unwrap() to assert this was a success let res = contract - .instantiate(deps.as_mut(), mock_env(), info, msg) + .instantiate(deps.as_mut(), env.clone(), info, msg) .unwrap(); assert_eq!(0, res.messages.len()); @@ -67,6 +73,8 @@ fn proper_instantiation() { CollectionInfo { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), + extension: None, + updated_at: env.block.time, } ); @@ -126,7 +134,7 @@ fn test_mint() { .unwrap(); assert_eq!( info, - NftInfoResponse:: { + NftInfoResponse:: { token_uri: Some(token_uri), extension: None, } diff --git a/contracts/cw721-expiration/src/execute.rs b/contracts/cw721-expiration/src/execute.rs index d698bf3e1..565f34382 100644 --- a/contracts/cw721-expiration/src/execute.rs +++ b/contracts/cw721-expiration/src/execute.rs @@ -1,12 +1,12 @@ use cosmwasm_std::{ Addr, Binary, Coin, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, Storage, }; -use cw721::{Cw721Execute, Expiration}; +use cw721::{Cw721Execute, EmptyCollectionInfoExtension, Expiration}; use cw721_base::Cw721Contract; use crate::{ error::ContractError, msg::ExecuteMsg, msg::InstantiateMsg, state::Cw721ExpirationContract, - Extension, + EmptyExtension, }; use cw721_base::InstantiateMsg as Cw721InstantiateMsg; @@ -16,7 +16,7 @@ impl<'a> Cw721ExpirationContract<'a> { deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { if msg.expiration_days == 0 { return Err(ContractError::MinExpiration {}); @@ -30,7 +30,9 @@ impl<'a> Cw721ExpirationContract<'a> { Cw721InstantiateMsg { name: msg.name, symbol: msg.symbol, + collection_info_extension: msg.extension, minter: msg.minter, + creator: msg.creator, withdraw_address: msg.withdraw_address, }, )?) @@ -95,7 +97,7 @@ impl<'a> Cw721ExpirationContract<'a> { token_id: String, owner: String, token_uri: Option, - extension: Extension, + extension: EmptyExtension, ) -> Result { let mint_timstamp = env.block.time; self.mint_timestamps @@ -113,11 +115,13 @@ impl<'a> Cw721ExpirationContract<'a> { info: MessageInfo, action: cw_ownable::Action, ) -> Result { - Ok( - Cw721Contract::::update_ownership( - deps, env, info, action, - )?, - ) + Ok(Cw721Contract::< + EmptyExtension, + Empty, + Empty, + Empty, + EmptyCollectionInfoExtension, + >::update_ownership(deps, env, info, action)?) } pub fn set_withdraw_address( diff --git a/contracts/cw721-expiration/src/lib.rs b/contracts/cw721-expiration/src/lib.rs index b42a0ee88..d8ce04260 100644 --- a/contracts/cw721-expiration/src/lib.rs +++ b/contracts/cw721-expiration/src/lib.rs @@ -14,9 +14,9 @@ const CONTRACT_NAME: &str = "crates.io:cw721-expiration"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub type MinterResponse = cw721_base::msg::MinterResponse; -pub use cw721_base::Extension; +pub use cw721_base::{EmptyCollectionInfoExtension, EmptyExtension}; -pub type NftInfo = cw721_base::state::NftInfo; +pub type NftInfo = cw721_base::state::NftInfo; #[deprecated(since = "0.19.0", note = "Please use NftInfo")] pub type TokenInfo = NftInfo; @@ -40,7 +40,7 @@ pub mod entry { mut deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; Cw721ExpirationContract::default().instantiate(deps.branch(), env, info, msg) @@ -87,9 +87,11 @@ mod tests { mock_info("mrt", &[]), InstantiateMsg { expiration_days: 0, - name: "".into(), - symbol: "".into(), - minter: Some("mrt".into()), + name: "collection_name".into(), + symbol: "collection_symbol".into(), + extension: None, + minter: Some("minter".into()), + creator: Some("creator".into()), withdraw_address: None, }, ) @@ -105,7 +107,9 @@ mod tests { expiration_days: 1, name: "".into(), symbol: "".into(), - minter: Some("mrt".into()), + extension: None, + minter: Some("minter".into()), + creator: Some("creator".into()), withdraw_address: None, }, ) diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index ef1f7f707..cb96088df 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -1,11 +1,11 @@ -use crate::{Extension, MinterResponse}; +use crate::{EmptyExtension, MinterResponse}; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::Empty; use cw_ownable::cw_ownable_query; -pub type ExecuteMsg = cw721_base::ExecuteMsg; +pub type ExecuteMsg = cw721_base::ExecuteMsg; #[cw_serde] -pub struct InstantiateMsg { +pub struct InstantiateMsg { /// max 65535 days pub expiration_days: u16, @@ -15,11 +15,16 @@ pub struct InstantiateMsg { /// Symbol of the NFT contract pub symbol: String, + pub extension: TCollectionInfoExtension, + /// The minter is the only one who can create new NFTs. /// This is designed for a base NFT that is controlled by an external program /// or contract. You will likely replace this with custom logic in custom NFTs pub minter: Option, + /// The creator is the only who can update collection info. + pub creator: Option, + pub withdraw_address: Option, } @@ -74,17 +79,17 @@ pub enum QueryMsg { NumTokens {}, #[deprecated(since = "0.19.0", note = "Please use CollectionInfo instead")] - #[returns(cw721::CollectionInfo)] + #[returns(cw721::CollectionInfo)] ContractInfo {}, /// With MetaData Extension. /// Returns top-level metadata about the contract - #[returns(cw721::CollectionInfo)] + #[returns(cw721::CollectionInfo)] CollectionInfo {}, /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract - #[returns(cw721::NftInfoResponse)] + #[returns(cw721::NftInfoResponse)] NftInfo { token_id: String, /// unset or false will filter out expired nfts, you must set to true to see them @@ -93,7 +98,7 @@ pub enum QueryMsg { /// With MetaData Extension. /// Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization /// for clients - #[returns(cw721::AllNftInfoResponse)] + #[returns(cw721::AllNftInfoResponse)] AllNftInfo { token_id: String, /// unset or false will filter out expired approvals, you must set to true to see them diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index 457975122..5c30f04f2 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -1,12 +1,12 @@ use cosmwasm_std::{to_json_binary, Addr, Binary, Deps, Env, StdResult}; use cw721::{ AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721Query, - NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, - TokensResponse, + EmptyCollectionInfoExtension, NftInfoResponse, NumTokensResponse, OperatorResponse, + OperatorsResponse, OwnerOfResponse, TokensResponse, }; use cw721_base::MinterResponse; -use crate::{error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract, Extension}; +use crate::{error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract, EmptyExtension}; impl<'a> Cw721ExpirationContract<'a> { pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> Result { @@ -135,11 +135,17 @@ impl<'a> Cw721ExpirationContract<'a> { // queries impl<'a> Cw721ExpirationContract<'a> { - pub fn contract_info(&self, deps: Deps) -> StdResult { + pub fn contract_info( + &self, + deps: Deps, + ) -> StdResult> { self.base_contract.collection_info(deps) } - pub fn collection_info(&self, deps: Deps) -> StdResult { + pub fn collection_info( + &self, + deps: Deps, + ) -> StdResult> { self.base_contract.collection_info.load(deps.storage) } @@ -153,7 +159,7 @@ impl<'a> Cw721ExpirationContract<'a> { env: Env, token_id: String, include_invalid: bool, - ) -> Result, ContractError> { + ) -> Result, ContractError> { if !include_invalid { self.assert_valid_nft(deps, &env, token_id.as_str())?; } @@ -291,7 +297,7 @@ impl<'a> Cw721ExpirationContract<'a> { token_id: String, include_expired: bool, include_invalid: bool, - ) -> Result, ContractError> { + ) -> Result, ContractError> { if !include_invalid { self.assert_valid_nft(deps, &env, token_id.as_str())?; } diff --git a/contracts/cw721-expiration/src/state.rs b/contracts/cw721-expiration/src/state.rs index c7ccb3d0d..50a94344b 100644 --- a/contracts/cw721-expiration/src/state.rs +++ b/contracts/cw721-expiration/src/state.rs @@ -1,12 +1,19 @@ use cosmwasm_std::{Empty, Timestamp}; use cw_storage_plus::{Item, Map}; -use crate::Extension; +use crate::{EmptyCollectionInfoExtension, EmptyExtension}; pub struct Cw721ExpirationContract<'a> { pub expiration_days: Item<'a, u16>, // max 65535 days pub mint_timestamps: Map<'a, &'a str, Timestamp>, - pub base_contract: cw721_base::Cw721Contract<'a, Extension, Empty, Empty, Empty>, + pub base_contract: cw721_base::Cw721Contract< + 'a, + EmptyExtension, + Empty, + Empty, + Empty, + EmptyCollectionInfoExtension, + >, } impl Default for Cw721ExpirationContract<'static> { diff --git a/contracts/cw721-fixed-price/examples/schema.rs b/contracts/cw721-fixed-price/examples/schema.rs index 4825e2b24..2da660b34 100644 --- a/contracts/cw721-fixed-price/examples/schema.rs +++ b/contracts/cw721-fixed-price/examples/schema.rs @@ -1,10 +1,11 @@ use cosmwasm_schema::write_api; +use cw721_base::EmptyCollectionInfoExtension; use cw721_fixed_price::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: InstantiateMsg, execute: ExecuteMsg, query: QueryMsg, } diff --git a/contracts/cw721-fixed-price/src/contract.rs b/contracts/cw721-fixed-price/src/contract.rs index 1b9d7a4f4..e0d65711e 100644 --- a/contracts/cw721-fixed-price/src/contract.rs +++ b/contracts/cw721-fixed-price/src/contract.rs @@ -11,6 +11,7 @@ use cosmwasm_std::{ }; use cw2::set_contract_version; use cw20::Cw20ReceiveMsg; +use cw721_base::EmptyCollectionInfoExtension; use cw721_base::{ helpers::Cw721Contract, msg::ExecuteMsg as Cw721ExecuteMsg, msg::InstantiateMsg as Cw721InstantiateMsg, @@ -28,7 +29,7 @@ pub fn instantiate( deps: DepsMut, _env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -61,7 +62,9 @@ pub fn instantiate( msg: to_json_binary(&Cw721InstantiateMsg { name: msg.name.clone(), symbol: msg.symbol, + collection_info_extension: msg.collection_info_extension, minter: None, + creator: None, withdraw_address: msg.withdraw_address, })?, funds: vec![], @@ -170,7 +173,8 @@ pub fn execute_receive( match config.cw721_address.clone() { Some(cw721) => { let callback = - Cw721Contract::(cw721, PhantomData, PhantomData).call(mint_msg)?; + Cw721Contract::(cw721, PhantomData, PhantomData, PhantomData) + .call(mint_msg)?; config.unused_token_id += 1; CONFIG.save(deps.storage, &config)?; @@ -185,7 +189,7 @@ mod tests { use super::*; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{from_json, to_json_binary, CosmosMsg, SubMsgResponse, SubMsgResult}; - use cw721_base::Extension; + use cw721_base::EmptyExtension; use prost::Message; const NFT_CONTRACT_ADDR: &str = "nftcontract"; @@ -208,6 +212,7 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), + collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -228,7 +233,9 @@ mod tests { msg: to_json_binary(&Cw721InstantiateMsg { name: msg.name.clone(), symbol: msg.symbol.clone(), + collection_info_extension: msg.collection_info_extension, minter: None, + creator: None, withdraw_address: None, }) .unwrap(), @@ -291,6 +298,7 @@ mod tests { unit_price: Uint128::new(0), name: String::from("SYNTH"), symbol: String::from("SYNTH"), + collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -316,6 +324,7 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), + collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -341,6 +350,7 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), + collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -378,7 +388,7 @@ mod tests { let info = mock_info(MOCK_CONTRACT_ADDR, &[]); let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - let mint_msg = Cw721ExecuteMsg::::Mint { + let mint_msg = Cw721ExecuteMsg::::Mint { token_id: String::from("0"), owner: String::from("minter"), token_uri: Some(String::from("https://ipfs.io/ipfs/Q")), @@ -409,6 +419,7 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), + collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -451,6 +462,7 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), + collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -495,6 +507,7 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), + collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -550,6 +563,7 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), + collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -585,6 +599,7 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), + collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -640,6 +655,7 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), + collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), diff --git a/contracts/cw721-fixed-price/src/msg.rs b/contracts/cw721-fixed-price/src/msg.rs index cdf723854..a180b0e86 100644 --- a/contracts/cw721-fixed-price/src/msg.rs +++ b/contracts/cw721-fixed-price/src/msg.rs @@ -1,10 +1,10 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, Uint128}; use cw20::Cw20ReceiveMsg; -use cw721_base::Extension; +use cw721_base::EmptyExtension; #[cw_serde] -pub struct InstantiateMsg { +pub struct InstantiateMsg { pub owner: Addr, pub max_tokens: u32, pub unit_price: Uint128, @@ -13,7 +13,8 @@ pub struct InstantiateMsg { pub token_code_id: u64, pub cw20_address: Addr, pub token_uri: String, - pub extension: Extension, + pub extension: EmptyExtension, + pub collection_info_extension: TCollectionInfoExtension, pub withdraw_address: Option, } @@ -39,6 +40,6 @@ pub struct ConfigResponse { pub name: String, pub symbol: String, pub token_uri: String, - pub extension: Extension, + pub extension: EmptyExtension, pub unused_token_id: u32, } diff --git a/contracts/cw721-fixed-price/src/state.rs b/contracts/cw721-fixed-price/src/state.rs index a1ad80c99..3cfa107c8 100644 --- a/contracts/cw721-fixed-price/src/state.rs +++ b/contracts/cw721-fixed-price/src/state.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Uint128}; -use cw721_base::Extension; +use cw721_base::EmptyExtension; use cw_storage_plus::Item; #[cw_serde] @@ -13,7 +13,7 @@ pub struct Config { pub name: String, pub symbol: String, pub token_uri: String, - pub extension: Extension, + pub extension: EmptyExtension, pub unused_token_id: u32, } diff --git a/contracts/cw721-metadata-onchain/examples/schema.rs b/contracts/cw721-metadata-onchain/examples/schema.rs index 36087d75d..3f1d0d140 100644 --- a/contracts/cw721-metadata-onchain/examples/schema.rs +++ b/contracts/cw721-metadata-onchain/examples/schema.rs @@ -1,10 +1,11 @@ use cosmwasm_schema::write_api; +use cw721::EmptyCollectionInfoExtension; use cw721_metadata_onchain::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: InstantiateMsg, execute: ExecuteMsg, query: QueryMsg, } diff --git a/contracts/cw721-metadata-onchain/src/lib.rs b/contracts/cw721-metadata-onchain/src/lib.rs index 11d053022..27056c676 100644 --- a/contracts/cw721-metadata-onchain/src/lib.rs +++ b/contracts/cw721-metadata-onchain/src/lib.rs @@ -1,37 +1,20 @@ -use cosmwasm_schema::cw_serde; use cosmwasm_std::Empty; -pub use cw721_base::{ContractError, InstantiateMsg, MinterResponse}; +use cw721::MetadataExtension; +pub use cw721_base::{ContractError, EmptyCollectionInfoExtension, InstantiateMsg, MinterResponse}; // Version info for migration const CONTRACT_NAME: &str = "crates.io:cw721-metadata-onchain"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -#[cw_serde] -pub struct Trait { - pub display_type: Option, - pub trait_type: String, - pub value: String, -} - -// see: https://docs.opensea.io/docs/metadata-standards -#[cw_serde] -#[derive(Default)] -pub struct Metadata { - pub image: Option, - pub image_data: Option, - pub external_url: Option, - pub description: Option, - pub name: Option, - pub attributes: Option>, - pub background_color: Option, - pub animation_url: Option, - pub youtube_url: Option, -} - -pub type Extension = Option; - -pub type Cw721MetadataContract<'a> = cw721_base::Cw721Contract<'a, Extension, Empty, Empty, Empty>; -pub type ExecuteMsg = cw721_base::ExecuteMsg; +pub type Cw721MetadataContract<'a> = cw721_base::Cw721Contract< + 'a, + MetadataExtension, + Empty, + Empty, + Empty, + EmptyCollectionInfoExtension, +>; +pub type ExecuteMsg = cw721_base::ExecuteMsg; pub type QueryMsg = cw721_base::QueryMsg; #[cfg(not(feature = "library"))] @@ -47,7 +30,7 @@ pub mod entry { mut deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -75,7 +58,7 @@ mod tests { use super::*; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cw721::Cw721Query; + use cw721::{Cw721Query, Metadata}; const CREATOR: &str = "creator"; @@ -90,9 +73,11 @@ mod tests { mock_env(), mock_info("larry", &[]), InstantiateMsg { - name: "".into(), - symbol: "".into(), - minter: None, + name: "collection_name".into(), + symbol: "collection_symbol".into(), + collection_info_extension: None, + minter: Some("minter".into()), + creator: Some("creator".into()), withdraw_address: None, }, ) @@ -110,9 +95,11 @@ mod tests { let info = mock_info(CREATOR, &[]); let init_msg = InstantiateMsg { - name: "SpaceShips".to_string(), - symbol: "SPACE".to_string(), + name: "collection_name".into(), + symbol: "collection_symbol".into(), + collection_info_extension: None, minter: None, + creator: None, withdraw_address: None, }; contract diff --git a/contracts/cw721-non-transferable/examples/schema.rs b/contracts/cw721-non-transferable/examples/schema.rs index f4239e3cf..15cf9625d 100644 --- a/contracts/cw721-non-transferable/examples/schema.rs +++ b/contracts/cw721-non-transferable/examples/schema.rs @@ -3,13 +3,14 @@ use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; +use cw721::EmptyCollectionInfoExtension; #[allow(deprecated)] use cw721::{ AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, ContractInfoResponse, Cw721ExecuteMsg, NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; -use cw721_non_transferable::{Extension, InstantiateMsg, MinterResponse, QueryMsg}; +use cw721_non_transferable::{EmptyExtension, InstantiateMsg, MinterResponse, QueryMsg}; fn main() { let mut out_dir = current_dir().unwrap(); @@ -17,11 +18,14 @@ fn main() { create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); - export_schema(&schema_for!(InstantiateMsg), &out_dir); + export_schema( + &schema_for!(InstantiateMsg), + &out_dir, + ); export_schema_with_title(&schema_for!(Cw721ExecuteMsg), &out_dir, "Cw721ExecuteMsg"); export_schema(&schema_for!(QueryMsg), &out_dir); export_schema_with_title( - &schema_for!(AllNftInfoResponse), + &schema_for!(AllNftInfoResponse), &out_dir, "AllNftInfoResponse", ); @@ -30,10 +34,13 @@ fn main() { export_schema(&schema_for!(OperatorsResponse), &out_dir); #[allow(deprecated)] export_schema(&schema_for!(ContractInfoResponse), &out_dir); - export_schema(&schema_for!(CollectionInfo), &out_dir); + export_schema( + &schema_for!(CollectionInfo), + &out_dir, + ); export_schema(&schema_for!(MinterResponse), &out_dir); export_schema_with_title( - &schema_for!(NftInfoResponse), + &schema_for!(NftInfoResponse), &out_dir, "NftInfoResponse", ); diff --git a/contracts/cw721-non-transferable/src/lib.rs b/contracts/cw721-non-transferable/src/lib.rs index e40fea0ba..c56956424 100644 --- a/contracts/cw721-non-transferable/src/lib.rs +++ b/contracts/cw721-non-transferable/src/lib.rs @@ -2,8 +2,8 @@ pub use crate::msg::{InstantiateMsg, QueryMsg}; use cosmwasm_std::Empty; pub use cw721_base::{ entry::{execute as _execute, query as _query}, - ContractError, Cw721Contract, ExecuteMsg, Extension, InstantiateMsg as Cw721BaseInstantiateMsg, - MinterResponse, + ContractError, Cw721Contract, EmptyCollectionInfoExtension, EmptyExtension, ExecuteMsg, + InstantiateMsg as Cw721BaseInstantiateMsg, MinterResponse, }; pub mod msg; @@ -14,7 +14,8 @@ pub mod state; const CONTRACT_NAME: &str = "crates.io:cw721-non-transferable"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -pub type Cw721NonTransferableContract<'a> = Cw721Contract<'a, Extension, Empty, Empty, Empty>; +pub type Cw721NonTransferableContract<'a> = + Cw721Contract<'a, EmptyExtension, Empty, Empty, Empty, EmptyCollectionInfoExtension>; #[cfg(not(feature = "library"))] pub mod entry { @@ -31,7 +32,7 @@ pub mod entry { mut deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { let admin_addr: Option = msg .admin @@ -46,7 +47,9 @@ pub mod entry { let cw721_base_instantiate_msg = Cw721BaseInstantiateMsg { name: msg.name, symbol: msg.symbol, + collection_info_extension: msg.collection_info_extension, minter: msg.minter, + creator: msg.creator, withdraw_address: msg.withdraw_address, }; @@ -69,7 +72,7 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: ExecuteMsg, + msg: ExecuteMsg, ) -> Result { let config = CONFIG.load(deps.storage)?; match config.admin { diff --git a/contracts/cw721-non-transferable/src/msg.rs b/contracts/cw721-non-transferable/src/msg.rs index 81e5ee761..97262a74a 100644 --- a/contracts/cw721-non-transferable/src/msg.rs +++ b/contracts/cw721-non-transferable/src/msg.rs @@ -3,11 +3,13 @@ use cosmwasm_std::Empty; use cw721_base::msg::QueryMsg as Cw721QueryMsg; #[cw_serde] -pub struct InstantiateMsg { +pub struct InstantiateMsg { pub admin: Option, pub name: String, pub symbol: String, + pub collection_info_extension: TCollectionInfoExtension, pub minter: Option, + pub creator: Option, pub withdraw_address: Option, } diff --git a/contracts/cw721-receiver-tester/tests/multitest.rs b/contracts/cw721-receiver-tester/tests/multitest.rs index a3c3b251d..ff2e535c4 100644 --- a/contracts/cw721-receiver-tester/tests/multitest.rs +++ b/contracts/cw721-receiver-tester/tests/multitest.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{to_json_binary, Addr, Attribute, Binary}; +use cosmwasm_std::{to_json_binary, Addr, Attribute, Binary, Empty}; use cw_multi_test::{App, ContractWrapper, Executor}; #[test] @@ -125,7 +125,9 @@ fn setup_contracts(app: &mut App, admin: Addr) -> Contracts { &base_msg::InstantiateMsg { name: "nft".to_string(), symbol: "NFT".to_string(), + collection_info_extension: Empty {}, minter: Some(admin.to_string()), + creator: Some(admin.to_string()), withdraw_address: None, }, &[], diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 88babd7f6..395c6bce4 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -2,7 +2,6 @@ use std::env::current_dir; use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; -use cosmwasm_std::Empty; #[allow(deprecated)] use cw721::{ @@ -10,9 +9,7 @@ use cw721::{ Cw721ExecuteMsg, Cw721QueryMsg, Cw721ReceiveMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; - -type Extension = Option; - +use cw721::{EmptyCollectionInfoExtension, EmptyExtension}; fn main() { let mut out_dir = current_dir().unwrap(); out_dir.push("schema"); @@ -23,7 +20,7 @@ fn main() { export_schema(&schema_for!(Cw721QueryMsg), &out_dir); export_schema(&schema_for!(Cw721ReceiveMsg), &out_dir); export_schema_with_title( - &schema_for!(AllNftInfoResponse), + &schema_for!(AllNftInfoResponse), &out_dir, "AllNftInfoResponse", ); @@ -33,10 +30,13 @@ fn main() { export_schema(&schema_for!(OperatorsResponse), &out_dir); #[allow(deprecated)] export_schema(&schema_for!(ContractInfoResponse), &out_dir); - export_schema(&schema_for!(CollectionInfo), &out_dir); + export_schema( + &schema_for!(CollectionInfo), + &out_dir, + ); export_schema(&schema_for!(OwnerOfResponse), &out_dir); export_schema_with_title( - &schema_for!(NftInfoResponse), + &schema_for!(NftInfoResponse), &out_dir, "NftInfoResponse", ); diff --git a/packages/cw721/src/lib.rs b/packages/cw721/src/lib.rs index cef9fbd73..b07fc3d82 100644 --- a/packages/cw721/src/lib.rs +++ b/packages/cw721/src/lib.rs @@ -4,6 +4,7 @@ mod receiver; mod state; mod traits; +use cosmwasm_std::Empty; pub use cw_utils::Expiration; pub use crate::msg::Cw721ExecuteMsg; @@ -14,5 +15,9 @@ pub use crate::query::{ OwnerOfResponse, TokensResponse, }; pub use crate::receiver::Cw721ReceiveMsg; -pub use crate::state::CollectionInfo; +pub use crate::state::{CollectionInfo, Metadata, MetadataExtension, Trait}; pub use crate::traits::{Cw721, Cw721Execute, Cw721Query}; + +// These are simple type to let us handle empty extensions +pub type EmptyExtension = Option; +pub type EmptyCollectionInfoExtension = Option; diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index 8c7f1a228..4929dd3ff 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -1,4 +1,5 @@ use cosmwasm_schema::cw_serde; +use cosmwasm_std::Empty; use cw_utils::Expiration; use crate::CollectionInfo; @@ -120,7 +121,8 @@ pub struct NumTokensResponse { #[deprecated(since = "0.19.0", note = "Please use CollectionInfoResponse instead")] #[allow(dead_code)] -pub type ContractInfoResponse = CollectionInfo; +pub type ContractInfoResponse = + CollectionInfo; #[cw_serde] pub struct NftInfoResponse { diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index 00ab4b498..376548dbf 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -1,7 +1,49 @@ use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Decimal, Timestamp}; #[cw_serde] -pub struct CollectionInfo { +pub struct CollectionInfo { pub name: String, pub symbol: String, + pub extension: TCollectionInfoExtension, + pub updated_at: Timestamp, } + +#[cw_serde] +pub struct CollectionInfoExtension { + pub description: Option, + pub image: Option, + pub external_link: Option, + pub explicit_content: Option, + pub royalty_info: Option, +} + +#[cw_serde] +pub struct RoyaltyInfo { + pub payment_address: Addr, + pub share: Decimal, +} + +// see: https://docs.opensea.io/docs/metadata-standards +#[cw_serde] +#[derive(Default)] +pub struct Metadata { + pub image: Option, + pub image_data: Option, + pub external_url: Option, + pub description: Option, + pub name: Option, + pub attributes: Option>, + pub background_color: Option, + pub animation_url: Option, + pub youtube_url: Option, +} + +#[cw_serde] +pub struct Trait { + pub display_type: Option, + pub trait_type: String, + pub value: String, +} + +pub type MetadataExtension = Option; diff --git a/packages/cw721/src/traits.rs b/packages/cw721/src/traits.rs index 6cd6ec332..66ee87fd3 100644 --- a/packages/cw721/src/traits.rs +++ b/packages/cw721/src/traits.rs @@ -8,11 +8,12 @@ use crate::{ use cosmwasm_std::{Binary, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; use cw_utils::Expiration; -pub trait Cw721: - Cw721Execute + Cw721Query +pub trait Cw721: + Cw721Execute + Cw721Query where TMetadata: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } @@ -87,14 +88,15 @@ where ) -> Result, Self::Err>; } -pub trait Cw721Query +pub trait Cw721Query where TMetadata: Serialize + DeserializeOwned + Clone, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { // TODO: use custom error? // How to handle the two derived error types? - fn collection_info(&self, deps: Deps) -> StdResult; + fn collection_info(&self, deps: Deps) -> StdResult>; fn num_tokens(&self, deps: Deps) -> StdResult; From ddc37588bfd7b664721734c45b7fc826cd9f81b4 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 29 Feb 2024 22:58:39 +0100 Subject: [PATCH 09/47] cargo schema --- .../schema/cw2981-royalties.json | 89 +++++++++++++-- contracts/cw721-base/schema/cw721-base.json | 89 +++++++++++++-- .../schema/cw721-expiration.json | 101 ++++++++++++++++-- .../cw721-expiration/src/contract_tests.rs | 4 +- contracts/cw721-expiration/src/execute.rs | 2 +- contracts/cw721-expiration/src/lib.rs | 4 +- contracts/cw721-expiration/src/msg.rs | 2 +- .../schema/cw721-fixed-price.json | 10 ++ .../schema/cw721-metadata-onchain.json | 89 +++++++++++++-- .../cw721-non-transferable/examples/schema.rs | 6 +- .../schema/collection_info.json | 50 +++++++++ .../schema/collection_info_for__empty.json | 44 ++++++++ .../schema/collection_info_response.json | 18 ---- .../schema/instantiate_msg.json | 24 ++++- packages/cw721/examples/schema.rs | 3 +- packages/cw721/schema/collection_info.json | 50 +++++++++ .../schema/collection_info_for__empty.json | 44 ++++++++ .../cw721/schema/contract_info_response.json | 18 ---- 18 files changed, 573 insertions(+), 74 deletions(-) create mode 100644 contracts/cw721-non-transferable/schema/collection_info.json create mode 100644 contracts/cw721-non-transferable/schema/collection_info_for__empty.json delete mode 100644 contracts/cw721-non-transferable/schema/collection_info_response.json create mode 100644 packages/cw721/schema/collection_info.json create mode 100644 packages/cw721/schema/collection_info_for__empty.json delete mode 100644 packages/cw721/schema/contract_info_response.json diff --git a/contracts/cw2981-royalties/schema/cw2981-royalties.json b/contracts/cw2981-royalties/schema/cw2981-royalties.json index 7bb9c8d7b..b0e5a4b1b 100644 --- a/contracts/cw2981-royalties/schema/cw2981-royalties.json +++ b/contracts/cw2981-royalties/schema/cw2981-royalties.json @@ -11,6 +11,23 @@ "symbol" ], "properties": { + "collection_info_extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, + "creator": { + "description": "The creator is the only who can update collection info.", + "type": [ + "string", + "null" + ] + }, "minter": { "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", "type": [ @@ -33,7 +50,13 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + } + } }, "execute": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -1581,39 +1604,91 @@ }, "collection_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfoResponse", + "title": "CollectionInfo_for_Empty", "type": "object", "required": [ + "extension", "name", - "symbol" + "symbol", + "updated_at" ], "properties": { + "extension": { + "$ref": "#/definitions/Empty" + }, "name": { "type": "string" }, "symbol": { "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfoResponse", + "title": "CollectionInfo_for_Empty", "type": "object", "required": [ + "extension", "name", - "symbol" + "symbol", + "updated_at" ], "properties": { + "extension": { + "$ref": "#/definitions/Empty" + }, "name": { "type": "string" }, "symbol": { "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } }, "extension": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/cw721-base/schema/cw721-base.json b/contracts/cw721-base/schema/cw721-base.json index 297d9bab8..9573ac851 100644 --- a/contracts/cw721-base/schema/cw721-base.json +++ b/contracts/cw721-base/schema/cw721-base.json @@ -11,6 +11,23 @@ "symbol" ], "properties": { + "collection_info_extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, + "creator": { + "description": "The creator is the only who can update collection info.", + "type": [ + "string", + "null" + ] + }, "minter": { "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", "type": [ @@ -33,7 +50,13 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + } + } }, "execute": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -1390,39 +1413,91 @@ }, "collection_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfoResponse", + "title": "CollectionInfo_for_Empty", "type": "object", "required": [ + "extension", "name", - "symbol" + "symbol", + "updated_at" ], "properties": { + "extension": { + "$ref": "#/definitions/Empty" + }, "name": { "type": "string" }, "symbol": { "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfoResponse", + "title": "CollectionInfo_for_Empty", "type": "object", "required": [ + "extension", "name", - "symbol" + "symbol", + "updated_at" ], "properties": { + "extension": { + "$ref": "#/definitions/Empty" + }, "name": { "type": "string" }, "symbol": { "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } }, "extension": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/cw721-expiration/schema/cw721-expiration.json b/contracts/cw721-expiration/schema/cw721-expiration.json index f10d77105..f03faf79b 100644 --- a/contracts/cw721-expiration/schema/cw721-expiration.json +++ b/contracts/cw721-expiration/schema/cw721-expiration.json @@ -12,6 +12,23 @@ "symbol" ], "properties": { + "collection_info_extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, + "creator": { + "description": "The creator is the only who can update collection info.", + "type": [ + "string", + "null" + ] + }, "expiration_days": { "description": "max 65535 days", "type": "integer", @@ -40,7 +57,13 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + } + } }, "execute": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -1435,39 +1458,103 @@ }, "collection_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfoResponse", + "title": "CollectionInfo_for_Nullable_Empty", "type": "object", "required": [ "name", - "symbol" + "symbol", + "updated_at" ], "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, "name": { "type": "string" }, "symbol": { "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfoResponse", + "title": "CollectionInfo_for_Nullable_Empty", "type": "object", "required": [ "name", - "symbol" + "symbol", + "updated_at" ], "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, "name": { "type": "string" }, "symbol": { "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } }, "extension": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/cw721-expiration/src/contract_tests.rs b/contracts/cw721-expiration/src/contract_tests.rs index 0e585f640..5bd570264 100644 --- a/contracts/cw721-expiration/src/contract_tests.rs +++ b/contracts/cw721-expiration/src/contract_tests.rs @@ -30,7 +30,7 @@ fn setup_contract(deps: DepsMut<'_>, expiration_days: u16) -> Cw721ExpirationCon expiration_days, name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - extension: None, + collection_info_extension: None, minter: Some(String::from(MINTER)), creator: Some(String::from(CREATOR)), withdraw_address: None, @@ -50,7 +50,7 @@ fn proper_instantiation() { expiration_days: 1, name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - extension: None, + collection_info_extension: None, minter: Some(String::from(MINTER)), creator: Some(String::from(CREATOR)), withdraw_address: None, diff --git a/contracts/cw721-expiration/src/execute.rs b/contracts/cw721-expiration/src/execute.rs index 565f34382..6c5618218 100644 --- a/contracts/cw721-expiration/src/execute.rs +++ b/contracts/cw721-expiration/src/execute.rs @@ -30,7 +30,7 @@ impl<'a> Cw721ExpirationContract<'a> { Cw721InstantiateMsg { name: msg.name, symbol: msg.symbol, - collection_info_extension: msg.extension, + collection_info_extension: msg.collection_info_extension, minter: msg.minter, creator: msg.creator, withdraw_address: msg.withdraw_address, diff --git a/contracts/cw721-expiration/src/lib.rs b/contracts/cw721-expiration/src/lib.rs index d8ce04260..0bea6c861 100644 --- a/contracts/cw721-expiration/src/lib.rs +++ b/contracts/cw721-expiration/src/lib.rs @@ -89,7 +89,7 @@ mod tests { expiration_days: 0, name: "collection_name".into(), symbol: "collection_symbol".into(), - extension: None, + collection_info_extension: None, minter: Some("minter".into()), creator: Some("creator".into()), withdraw_address: None, @@ -107,7 +107,7 @@ mod tests { expiration_days: 1, name: "".into(), symbol: "".into(), - extension: None, + collection_info_extension: None, minter: Some("minter".into()), creator: Some("creator".into()), withdraw_address: None, diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index cb96088df..61d68b5cc 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -15,7 +15,7 @@ pub struct InstantiateMsg { /// Symbol of the NFT contract pub symbol: String, - pub extension: TCollectionInfoExtension, + pub collection_info_extension: TCollectionInfoExtension, /// The minter is the only one who can create new NFTs. /// This is designed for a base NFT that is controlled by an external program diff --git a/contracts/cw721-fixed-price/schema/cw721-fixed-price.json b/contracts/cw721-fixed-price/schema/cw721-fixed-price.json index ff2ae78b8..69db020ec 100644 --- a/contracts/cw721-fixed-price/schema/cw721-fixed-price.json +++ b/contracts/cw721-fixed-price/schema/cw721-fixed-price.json @@ -17,6 +17,16 @@ "unit_price" ], "properties": { + "collection_info_extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, "cw20_address": { "$ref": "#/definitions/Addr" }, diff --git a/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json b/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json index 75e86dd1b..c2b6067fa 100644 --- a/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json +++ b/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json @@ -11,6 +11,23 @@ "symbol" ], "properties": { + "collection_info_extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, + "creator": { + "description": "The creator is the only who can update collection info.", + "type": [ + "string", + "null" + ] + }, "minter": { "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", "type": [ @@ -33,7 +50,13 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + } + } }, "execute": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -1477,39 +1500,91 @@ }, "collection_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfoResponse", + "title": "CollectionInfo_for_Empty", "type": "object", "required": [ + "extension", "name", - "symbol" + "symbol", + "updated_at" ], "properties": { + "extension": { + "$ref": "#/definitions/Empty" + }, "name": { "type": "string" }, "symbol": { "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfoResponse", + "title": "CollectionInfo_for_Empty", "type": "object", "required": [ + "extension", "name", - "symbol" + "symbol", + "updated_at" ], "properties": { + "extension": { + "$ref": "#/definitions/Empty" + }, "name": { "type": "string" }, "symbol": { "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } }, "extension": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/cw721-non-transferable/examples/schema.rs b/contracts/cw721-non-transferable/examples/schema.rs index 15cf9625d..601570c75 100644 --- a/contracts/cw721-non-transferable/examples/schema.rs +++ b/contracts/cw721-non-transferable/examples/schema.rs @@ -18,9 +18,10 @@ fn main() { create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); - export_schema( + export_schema_with_title( &schema_for!(InstantiateMsg), &out_dir, + "InstantiateMsg", ); export_schema_with_title(&schema_for!(Cw721ExecuteMsg), &out_dir, "Cw721ExecuteMsg"); export_schema(&schema_for!(QueryMsg), &out_dir); @@ -34,9 +35,10 @@ fn main() { export_schema(&schema_for!(OperatorsResponse), &out_dir); #[allow(deprecated)] export_schema(&schema_for!(ContractInfoResponse), &out_dir); - export_schema( + export_schema_with_title( &schema_for!(CollectionInfo), &out_dir, + "CollectionInfo", ); export_schema(&schema_for!(MinterResponse), &out_dir); export_schema_with_title( diff --git a/contracts/cw721-non-transferable/schema/collection_info.json b/contracts/cw721-non-transferable/schema/collection_info.json new file mode 100644 index 000000000..431c4d037 --- /dev/null +++ b/contracts/cw721-non-transferable/schema/collection_info.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfo", + "type": "object", + "required": [ + "name", + "symbol", + "updated_at" + ], + "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/cw721-non-transferable/schema/collection_info_for__empty.json b/contracts/cw721-non-transferable/schema/collection_info_for__empty.json new file mode 100644 index 000000000..5d82f6687 --- /dev/null +++ b/contracts/cw721-non-transferable/schema/collection_info_for__empty.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfo_for_Empty", + "type": "object", + "required": [ + "extension", + "name", + "symbol", + "updated_at" + ], + "properties": { + "extension": { + "$ref": "#/definitions/Empty" + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/cw721-non-transferable/schema/collection_info_response.json b/contracts/cw721-non-transferable/schema/collection_info_response.json deleted file mode 100644 index 84822f8b1..000000000 --- a/contracts/cw721-non-transferable/schema/collection_info_response.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfoResponse", - "type": "object", - "required": [ - "name", - "symbol" - ], - "properties": { - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - } - }, - "additionalProperties": false -} diff --git a/contracts/cw721-non-transferable/schema/instantiate_msg.json b/contracts/cw721-non-transferable/schema/instantiate_msg.json index cb5c43020..db962cae7 100644 --- a/contracts/cw721-non-transferable/schema/instantiate_msg.json +++ b/contracts/cw721-non-transferable/schema/instantiate_msg.json @@ -13,6 +13,22 @@ "null" ] }, + "collection_info_extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, + "creator": { + "type": [ + "string", + "null" + ] + }, "minter": { "type": [ "string", @@ -32,5 +48,11 @@ ] } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + } + } } diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 395c6bce4..0268bba03 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -30,9 +30,10 @@ fn main() { export_schema(&schema_for!(OperatorsResponse), &out_dir); #[allow(deprecated)] export_schema(&schema_for!(ContractInfoResponse), &out_dir); - export_schema( + export_schema_with_title( &schema_for!(CollectionInfo), &out_dir, + "CollectionInfo", ); export_schema(&schema_for!(OwnerOfResponse), &out_dir); export_schema_with_title( diff --git a/packages/cw721/schema/collection_info.json b/packages/cw721/schema/collection_info.json new file mode 100644 index 000000000..431c4d037 --- /dev/null +++ b/packages/cw721/schema/collection_info.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfo", + "type": "object", + "required": [ + "name", + "symbol", + "updated_at" + ], + "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/packages/cw721/schema/collection_info_for__empty.json b/packages/cw721/schema/collection_info_for__empty.json new file mode 100644 index 000000000..5d82f6687 --- /dev/null +++ b/packages/cw721/schema/collection_info_for__empty.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfo_for_Empty", + "type": "object", + "required": [ + "extension", + "name", + "symbol", + "updated_at" + ], + "properties": { + "extension": { + "$ref": "#/definitions/Empty" + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false, + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/packages/cw721/schema/contract_info_response.json b/packages/cw721/schema/contract_info_response.json deleted file mode 100644 index 4a805a825..000000000 --- a/packages/cw721/schema/contract_info_response.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ContractInfoResponse", - "type": "object", - "required": [ - "name", - "symbol" - ], - "properties": { - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - } - }, - "additionalProperties": false -} From 18dd4d3f5d28990bda58d54b6937608791cb9d2b Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 29 Feb 2024 23:08:17 +0100 Subject: [PATCH 10/47] typo --- contracts/cw721-base/src/lib.rs | 2 +- packages/cw721/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index e4efc59f0..801e1a60a 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -24,7 +24,7 @@ pub use cw_ownable::{Action, Ownership, OwnershipError}; use cosmwasm_std::Empty; -// These are simple type to let us handle empty extensions +// These are simple types to let us handle empty extensions pub use cw721::EmptyCollectionInfoExtension; pub use cw721::EmptyExtension; diff --git a/packages/cw721/src/lib.rs b/packages/cw721/src/lib.rs index b07fc3d82..d0d2af686 100644 --- a/packages/cw721/src/lib.rs +++ b/packages/cw721/src/lib.rs @@ -18,6 +18,6 @@ pub use crate::receiver::Cw721ReceiveMsg; pub use crate::state::{CollectionInfo, Metadata, MetadataExtension, Trait}; pub use crate::traits::{Cw721, Cw721Execute, Cw721Query}; -// These are simple type to let us handle empty extensions +// These are simple types to let us handle empty extensions pub type EmptyExtension = Option; pub type EmptyCollectionInfoExtension = Option; From 917103c80a4bd4229a741ae70cfba328cc601c04 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 29 Feb 2024 23:13:09 +0100 Subject: [PATCH 11/47] cleanup --- contracts/cw721-base/src/helpers.rs | 8 -------- contracts/cw721-base/src/state.rs | 3 --- contracts/cw721-expiration/src/lib.rs | 3 --- packages/cw721/src/lib.rs | 6 +++--- packages/cw721/src/query.rs | 5 ----- 5 files changed, 3 insertions(+), 22 deletions(-) diff --git a/contracts/cw721-base/src/helpers.rs b/contracts/cw721-base/src/helpers.rs index 2bfa8877e..2dbb53b94 100644 --- a/contracts/cw721-base/src/helpers.rs +++ b/contracts/cw721-base/src/helpers.rs @@ -129,14 +129,6 @@ impl( - &self, - querier: &QuerierWrapper, - ) -> StdResult> { - self.collection_info(querier) - } - /// With metadata extension pub fn collection_info( &self, diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index a963ba7d6..79a70c901 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -159,9 +159,6 @@ where } } -#[deprecated(since = "0.19.0", note = "Please use NftInfo")] -pub type TokenInfo = NftInfo; - #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct NftInfo { /// The owner of the newly minted NFT diff --git a/contracts/cw721-expiration/src/lib.rs b/contracts/cw721-expiration/src/lib.rs index 0bea6c861..260bd2572 100644 --- a/contracts/cw721-expiration/src/lib.rs +++ b/contracts/cw721-expiration/src/lib.rs @@ -18,9 +18,6 @@ pub use cw721_base::{EmptyCollectionInfoExtension, EmptyExtension}; pub type NftInfo = cw721_base::state::NftInfo; -#[deprecated(since = "0.19.0", note = "Please use NftInfo")] -pub type TokenInfo = NftInfo; - pub mod entry { use crate::{ error::ContractError, diff --git a/packages/cw721/src/lib.rs b/packages/cw721/src/lib.rs index d0d2af686..378eb9430 100644 --- a/packages/cw721/src/lib.rs +++ b/packages/cw721/src/lib.rs @@ -10,9 +10,9 @@ pub use cw_utils::Expiration; pub use crate::msg::Cw721ExecuteMsg; #[allow(deprecated)] pub use crate::query::{ - AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, - Cw721QueryMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, - OwnerOfResponse, TokensResponse, + AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, Cw721QueryMsg, + NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, + TokensResponse, }; pub use crate::receiver::Cw721ReceiveMsg; pub use crate::state::{CollectionInfo, Metadata, MetadataExtension, Trait}; diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index 4929dd3ff..155af3ea3 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -119,11 +119,6 @@ pub struct NumTokensResponse { pub count: u64, } -#[deprecated(since = "0.19.0", note = "Please use CollectionInfoResponse instead")] -#[allow(dead_code)] -pub type ContractInfoResponse = - CollectionInfo; - #[cw_serde] pub struct NftInfoResponse { /// Universal resource identifier for this NFT From b3aac9ef69afc129bfe53d542dccc81650003f7f Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 29 Feb 2024 23:16:30 +0100 Subject: [PATCH 12/47] rename --- contracts/cw721-base/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index 801e1a60a..cfd275f92 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -87,7 +87,7 @@ pub mod entry { pub fn migrate(deps: DepsMut, env: Env, msg: Empty) -> Result { let response = migrate_version(deps.storage, &env, &msg)?; let response = migrate_legacy_minter(deps.storage, deps.api, &env, &msg, response)?; - let response = migrate_legacy_contract_info(deps.storage, &env, &msg, response)?; + let response = migrate_legacy_collection_info(deps.storage, &env, &msg, response)?; migrate_legacy_tokens(deps.storage, &env, &msg, response) } @@ -130,7 +130,7 @@ pub mod entry { } /// Migrates only in case collection_info is not present - pub fn migrate_legacy_contract_info( + pub fn migrate_legacy_collection_info( storage: &mut dyn Storage, env: &Env, _msg: &Empty, @@ -146,6 +146,7 @@ pub mod entry { match contract.collection_info.may_load(storage)? { Some(_) => Ok(response), None => { + // contract info is legacy collection info let legacy_collection_info_store: Item = Item::new("nft_info"); let legacy_collection_info = legacy_collection_info_store.load(storage)?; From 8d61f85b50abbeb5536c69fedeaae64721ed2619 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 29 Feb 2024 23:28:13 +0100 Subject: [PATCH 13/47] cargo schema --- .../cw721-non-transferable/examples/schema.rs | 4 +- .../schema/collection_info_for__empty.json | 44 ------------------- packages/cw721/examples/schema.rs | 5 +-- .../schema/collection_info_for__empty.json | 44 ------------------- 4 files changed, 3 insertions(+), 94 deletions(-) delete mode 100644 contracts/cw721-non-transferable/schema/collection_info_for__empty.json delete mode 100644 packages/cw721/schema/collection_info_for__empty.json diff --git a/contracts/cw721-non-transferable/examples/schema.rs b/contracts/cw721-non-transferable/examples/schema.rs index 601570c75..a388d6536 100644 --- a/contracts/cw721-non-transferable/examples/schema.rs +++ b/contracts/cw721-non-transferable/examples/schema.rs @@ -6,7 +6,7 @@ use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, s use cw721::EmptyCollectionInfoExtension; #[allow(deprecated)] use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, ContractInfoResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721ExecuteMsg, NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; @@ -33,8 +33,6 @@ fn main() { export_schema(&schema_for!(ApprovalResponse), &out_dir); export_schema(&schema_for!(ApprovalsResponse), &out_dir); export_schema(&schema_for!(OperatorsResponse), &out_dir); - #[allow(deprecated)] - export_schema(&schema_for!(ContractInfoResponse), &out_dir); export_schema_with_title( &schema_for!(CollectionInfo), &out_dir, diff --git a/contracts/cw721-non-transferable/schema/collection_info_for__empty.json b/contracts/cw721-non-transferable/schema/collection_info_for__empty.json deleted file mode 100644 index 5d82f6687..000000000 --- a/contracts/cw721-non-transferable/schema/collection_info_for__empty.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Empty", - "type": "object", - "required": [ - "extension", - "name", - "symbol", - "updated_at" - ], - "properties": { - "extension": { - "$ref": "#/definitions/Empty" - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } -} diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 0268bba03..f4775674e 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -3,9 +3,10 @@ use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; +use cosmwasm_std::Empty; #[allow(deprecated)] use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, ContractInfoResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721ExecuteMsg, Cw721QueryMsg, Cw721ReceiveMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; @@ -28,8 +29,6 @@ fn main() { export_schema(&schema_for!(ApprovalsResponse), &out_dir); export_schema(&schema_for!(OperatorResponse), &out_dir); export_schema(&schema_for!(OperatorsResponse), &out_dir); - #[allow(deprecated)] - export_schema(&schema_for!(ContractInfoResponse), &out_dir); export_schema_with_title( &schema_for!(CollectionInfo), &out_dir, diff --git a/packages/cw721/schema/collection_info_for__empty.json b/packages/cw721/schema/collection_info_for__empty.json deleted file mode 100644 index 5d82f6687..000000000 --- a/packages/cw721/schema/collection_info_for__empty.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Empty", - "type": "object", - "required": [ - "extension", - "name", - "symbol", - "updated_at" - ], - "properties": { - "extension": { - "$ref": "#/definitions/Empty" - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } -} From 89da1ab8769b86bc7c745febbf20cccc1147f8af Mon Sep 17 00:00:00 2001 From: mr-t Date: Sun, 3 Mar 2024 19:54:42 +0100 Subject: [PATCH 14/47] cleanup --- contracts/cw721-base/src/lib.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index cfd275f92..9115e0afa 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -85,20 +85,21 @@ pub mod entry { #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(deps: DepsMut, env: Env, msg: Empty) -> Result { - let response = migrate_version(deps.storage, &env, &msg)?; + let response = Response::::default(); let response = migrate_legacy_minter(deps.storage, deps.api, &env, &msg, response)?; let response = migrate_legacy_collection_info(deps.storage, &env, &msg, response)?; - migrate_legacy_tokens(deps.storage, &env, &msg, response) + let response = migrate_legacy_tokens(deps.storage, &env, &msg, response)?; + let response = migrate_version(deps.storage, &env, &msg, response)?; + Ok(response) } pub fn migrate_version( storage: &mut dyn Storage, _env: &Env, _msg: &Empty, - ) -> Result { - // make sure the correct contract is being upgraded, and it's being - // upgraded from the correct version. - let response = Response::::default() + response: Response, + ) -> StdResult { + let response = response .add_attribute("from_version", cw2::get_contract_version(storage)?.version) .add_attribute("to_version", CONTRACT_VERSION); @@ -170,7 +171,7 @@ pub mod entry { _env: &Env, _msg: &Empty, response: Response, - ) -> Result { + ) -> StdResult { let contract = Cw721Contract::::default(); match contract.nft_info.is_empty(storage) { false => Ok(response), From fcd5159346090907edb7a5e7828a1f634df7e30f Mon Sep 17 00:00:00 2001 From: mr-t Date: Mon, 4 Mar 2024 14:14:18 +0100 Subject: [PATCH 15/47] creator and owner changes: - new query GetMinterOwnership and GetCreatorOwnership, deprecated Ownership - new execute UpdateMinterOwnership and UpdateCreatorOwnership, deprecate UpdateOwnership - dedicated stores for minter and creator, where creator usess by default cw_ownable singleton! - new migrate msg allowing to reset creator and minter - cleanup migration and split legacy part to dedicated functions - also make sure using decicated CREATOR and MINTER stores and NOT use cw_ownable::... --- Cargo.lock | 141 +++- Cargo.toml | 8 +- contracts/cw721-base/Cargo.toml | 6 +- contracts/cw721-base/src/contract_tests.rs | 122 ++-- contracts/cw721-base/src/execute.rs | 92 ++- contracts/cw721-base/src/helpers.rs | 2 +- contracts/cw721-base/src/lib.rs | 153 +++- contracts/cw721-base/src/msg.rs | 66 +- contracts/cw721-base/src/multi_tests.rs | 687 ++++++++++++++++-- contracts/cw721-base/src/query.rs | 28 +- contracts/cw721-base/src/state.rs | 10 +- .../cw721-expiration/src/contract_tests.rs | 24 +- contracts/cw721-expiration/src/execute.rs | 33 +- contracts/cw721-expiration/src/msg.rs | 21 +- contracts/cw721-expiration/src/query.rs | 27 +- .../cw721-non-transferable/examples/schema.rs | 5 +- contracts/cw721-non-transferable/src/msg.rs | 22 +- packages/cw721/examples/schema.rs | 6 +- 18 files changed, 1232 insertions(+), 221 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 282e67556..43e11131a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,6 +204,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cw-address-like" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451a4691083a88a3c0630a8a88799e9d4cd6679b7ce8ff22b8da2873ff31d380" +dependencies = [ + "cosmwasm-std", +] + [[package]] name = "cw-address-like" version = "1.0.4" @@ -232,6 +241,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw-ownable" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093dfb4520c48b5848274dd88ea99e280a04bc08729603341c7fb0d758c74321" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-address-like 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cw-ownable-derive 0.5.1", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "thiserror", +] + [[package]] name = "cw-ownable" version = "0.6.0" @@ -239,13 +263,24 @@ source = "git+https://github.com/public-awesome/cw-plus-plus.git?branch=multiple dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-address-like", - "cw-ownable-derive", + "cw-address-like 1.0.4 (git+https://github.com/public-awesome/cw-plus-plus.git?branch=multiple_ownership)", + "cw-ownable-derive 0.6.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "thiserror", ] +[[package]] +name = "cw-ownable-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d3bf2e0f341bb6cc100d7d441d31cf713fbd3ce0c511f91e79f14b40a889af" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "cw-ownable-derive" version = "0.6.0" @@ -376,6 +411,42 @@ dependencies = [ "serde", ] +[[package]] +name = "cw721" +version = "0.16.0" +source = "git+https://github.com/CosmWasm/cw-nfts?tag=v0.16.0#2cad1d3e15e0a34d466a0b51e02c58b82ebe5ecd" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 0.16.0", + "schemars", + "serde", +] + +[[package]] +name = "cw721" +version = "0.17.0" +source = "git+https://github.com/CosmWasm/cw-nfts?tag=v0.17.0#c1ece555dded6cbcebba1d417ed2a18d47ca3c8a" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 1.0.3", + "schemars", + "serde", +] + +[[package]] +name = "cw721" +version = "0.18.0" +source = "git+https://github.com/CosmWasm/cw-nfts?tag=v0.18.0#177a993dfb5a1a3164be1baf274f43b1ca53da53" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils 1.0.3", + "schemars", + "serde", +] + [[package]] name = "cw721" version = "0.19.0" @@ -398,7 +469,59 @@ dependencies = [ "cw-storage-plus 0.16.0", "cw-utils 0.16.0", "cw2 0.16.0", - "cw721 0.16.0", + "cw721 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw721-base" +version = "0.16.0" +source = "git+https://github.com/CosmWasm/cw-nfts?tag=v0.16.0#2cad1d3e15e0a34d466a0b51e02c58b82ebe5ecd" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 0.16.0", + "cw-utils 0.16.0", + "cw2 0.16.0", + "cw721 0.16.0 (git+https://github.com/CosmWasm/cw-nfts?tag=v0.16.0)", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw721-base" +version = "0.17.0" +source = "git+https://github.com/CosmWasm/cw-nfts?tag=v0.17.0#c1ece555dded6cbcebba1d417ed2a18d47ca3c8a" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-ownable 0.5.1", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw721 0.17.0", + "cw721-base 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw721-base" +version = "0.18.0" +source = "git+https://github.com/CosmWasm/cw-nfts?tag=v0.18.0#177a993dfb5a1a3164be1baf274f43b1ca53da53" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-ownable 0.5.1", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw2 1.1.2", + "cw721 0.18.0", + "cw721-base 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "schemars", "serde", "thiserror", @@ -411,13 +534,17 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", - "cw-ownable", + "cw-ownable 0.6.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", - "cw721 0.16.0", + "cw721 0.16.0 (git+https://github.com/CosmWasm/cw-nfts?tag=v0.16.0)", + "cw721 0.17.0", + "cw721 0.18.0", "cw721 0.19.0", - "cw721-base 0.16.0", + "cw721-base 0.16.0 (git+https://github.com/CosmWasm/cw-nfts?tag=v0.16.0)", + "cw721-base 0.17.0", + "cw721-base 0.18.0", "schemars", "serde", "thiserror", @@ -430,7 +557,7 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", - "cw-ownable", + "cw-ownable 0.6.0", "cw-storage-plus 1.2.0", "cw2 1.1.2", "cw721 0.19.0", diff --git a/Cargo.toml b/Cargo.toml index dfca3e6d4..226184477 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,13 @@ cosmwasm-std = "^1.2" cw2 = "^1.1" cw20 = "^1.1" cw721 = { version = "*", path = "./packages/cw721" } -cw721-016 = { version = "0.16.0", package = "cw721" } +cw721-016 = { git = "https://github.com/CosmWasm/cw-nfts", tag = "v0.16.0", package = "cw721" } # needed for backwards compatibility and legacy migration +cw721-017 = { git = "https://github.com/CosmWasm/cw-nfts", tag = "v0.17.0", package = "cw721" } # needed for testing legacy migration +cw721-018 = { git = "https://github.com/CosmWasm/cw-nfts", tag = "v0.18.0", package = "cw721" } # needed for testing legacy migration cw721-base = { version = "*", path = "./contracts/cw721-base" } -cw721-base-016 = { version = "0.16.0", package = "cw721-base" } +cw721-base-016 = { git = "https://github.com/CosmWasm/cw-nfts", tag = "v0.16.0", package = "cw721-base" } # needed for testing legacy migration +cw721-base-017 = { git = "https://github.com/CosmWasm/cw-nfts", tag = "v0.17.0", package = "cw721-base" } # needed for testing legacy migration +cw721-base-018 = { git = "https://github.com/CosmWasm/cw-nfts", tag = "v0.18.0", package = "cw721-base" } # needed for testing legacy migration cw-multi-test = "^0.20" cw-ownable = { git = "https://github.com/public-awesome/cw-plus-plus.git", branch = "multiple_ownership"} # TODO: switch to official https://github.com/larry0x/cw-plus-plus once merged cw-storage-plus = "^1.1" diff --git a/contracts/cw721-base/Cargo.toml b/contracts/cw721-base/Cargo.toml index 0058d7d6a..562e52b5d 100644 --- a/contracts/cw721-base/Cargo.toml +++ b/contracts/cw721-base/Cargo.toml @@ -32,10 +32,14 @@ cw-utils = { workspace = true } cw2 = { workspace = true } cw721 = { workspace = true } cw721-016 = { workspace = true } -cw721-base-016 = { workspace = true, features = ["library"] } schemars = { workspace = true } serde = { workspace = true } thiserror = { workspace = true } [dev-dependencies] cw-multi-test = { workspace = true } +cw721-017 = { workspace = true } +cw721-018 = { workspace = true } +cw721-base-016 = { workspace = true, features = ["library"] } +cw721-base-017 = { workspace = true, features = ["library"] } +cw721-base-018 = { workspace = true, features = ["library"] } diff --git a/contracts/cw721-base/src/contract_tests.rs b/contracts/cw721-base/src/contract_tests.rs index 1ce32b55f..98729f490 100644 --- a/contracts/cw721-base/src/contract_tests.rs +++ b/contracts/cw721-base/src/contract_tests.rs @@ -9,15 +9,13 @@ use cw721::{ Approval, ApprovalResponse, CollectionInfo, Cw721Query, Cw721ReceiveMsg, Expiration, NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, }; -use cw_ownable::OwnershipError; +use cw_ownable::{Action, Ownership, OwnershipError}; -use crate::{ - ContractError, Cw721Contract, EmptyExtension, ExecuteMsg, InstantiateMsg, MinterResponse, - QueryMsg, -}; +use crate::state::{CREATOR, MINTER}; +use crate::{ContractError, Cw721Contract, EmptyExtension, ExecuteMsg, InstantiateMsg, QueryMsg}; -const MINTER: &str = "minter"; -const CREATOR: &str = "creator"; +const MINTER_ADDR: &str = "minter"; +const CREATOR_ADDR: &str = "creator"; const CONTRACT_NAME: &str = "Magic Power"; const SYMBOL: &str = "MGK"; @@ -29,8 +27,8 @@ fn setup_contract( name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), collection_info_extension: Empty {}, - minter: Some(String::from(MINTER)), - creator: Some(String::from(CREATOR)), + minter: Some(String::from(MINTER_ADDR)), + creator: Some(String::from(CREATOR_ADDR)), withdraw_address: None, }; let info = mock_info("creator", &[]); @@ -48,9 +46,9 @@ fn proper_instantiation() { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), collection_info_extension: Empty {}, - minter: Some(String::from(MINTER)), - creator: Some(String::from(CREATOR)), - withdraw_address: Some(String::from(MINTER)), + minter: Some(String::from(MINTER_ADDR)), + creator: Some(String::from(CREATOR_ADDR)), + withdraw_address: Some(String::from(CREATOR_ADDR)), }; let info = mock_info("creator", &[]); let env = mock_env(); @@ -62,8 +60,10 @@ fn proper_instantiation() { assert_eq!(0, res.messages.len()); // it worked, let's query the state - let res = contract.minter(deps.as_ref()).unwrap(); - assert_eq!(Some(MINTER.to_string()), res.minter); + let minter_ownership = MINTER.get_ownership(deps.as_ref().storage).unwrap(); + assert_eq!(Some(Addr::unchecked(MINTER_ADDR)), minter_ownership.owner); + let creator_ownership = CREATOR.get_ownership(deps.as_ref().storage).unwrap(); + assert_eq!(Some(Addr::unchecked(CREATOR_ADDR)), creator_ownership.owner); let info = contract.collection_info(deps.as_ref()).unwrap(); assert_eq!( info, @@ -79,7 +79,7 @@ fn proper_instantiation() { .withdraw_address .may_load(deps.as_ref().storage) .unwrap(); - assert_eq!(Some(MINTER.to_string()), withdraw_address); + assert_eq!(Some(CREATOR_ADDR.to_string()), withdraw_address); let count = contract.num_tokens(deps.as_ref()).unwrap(); assert_eq!(0, count.count); @@ -112,7 +112,7 @@ fn minting() { assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); // minter can mint - let allowed = mock_info(MINTER, &[]); + let allowed = mock_info(MINTER_ADDR, &[]); let _ = contract .execute(deps.as_mut(), mock_env(), allowed, mint_msg) .unwrap(); @@ -156,7 +156,7 @@ fn minting() { extension: None, }; - let allowed = mock_info(MINTER, &[]); + let allowed = mock_info(MINTER_ADDR, &[]); let err = contract .execute(deps.as_mut(), mock_env(), allowed, mint_msg2) .unwrap_err(); @@ -184,7 +184,7 @@ fn test_update_minter() { }; // Minter can mint - let minter_info = mock_info(MINTER, &[]); + let minter_info = mock_info(MINTER_ADDR, &[]); let _ = contract .execute(deps.as_mut(), mock_env(), minter_info.clone(), mint_msg) .unwrap(); @@ -196,7 +196,7 @@ fn test_update_minter() { deps.as_mut(), mock_env(), minter_info.clone(), - ExecuteMsg::UpdateOwnership(cw_ownable::Action::TransferOwnership { + ExecuteMsg::UpdateMinterOwnership(Action::TransferOwnership { new_owner: "random".to_string(), expiry: None, }), @@ -204,26 +204,18 @@ fn test_update_minter() { .unwrap(); // Minter does not change until ownership transfer completes. - let minter: MinterResponse = from_json( - contract - .query(deps.as_ref(), mock_env(), QueryMsg::Minter {}) - .unwrap(), - ) - .unwrap(); - assert_eq!(minter.minter, Some(MINTER.to_string())); - // Pending ownership transfer should be discoverable via query. - let ownership: cw_ownable::Ownership = from_json( + let ownership: Ownership = from_json( contract - .query(deps.as_ref(), mock_env(), QueryMsg::Ownership {}) + .query(deps.as_ref(), mock_env(), QueryMsg::GetMinterOwnership {}) .unwrap(), ) .unwrap(); assert_eq!( ownership, - cw_ownable::Ownership:: { - owner: Some(Addr::unchecked(MINTER)), + Ownership:: { + owner: Some(Addr::unchecked(MINTER_ADDR)), pending_owner: Some(Addr::unchecked("random")), pending_expiry: None, } @@ -236,18 +228,18 @@ fn test_update_minter() { deps.as_mut(), mock_env(), random_info.clone(), - ExecuteMsg::UpdateOwnership(cw_ownable::Action::AcceptOwnership), + ExecuteMsg::UpdateMinterOwnership(Action::AcceptOwnership), ) .unwrap(); // Minter changes after ownership transfer is accepted. - let minter: MinterResponse = from_json( + let minter_ownership: Ownership = from_json( contract - .query(deps.as_ref(), mock_env(), QueryMsg::Minter {}) + .query(deps.as_ref(), mock_env(), QueryMsg::GetMinterOwnership {}) .unwrap(), ) .unwrap(); - assert_eq!(minter.minter, Some("random".to_string())); + assert_eq!(minter_ownership.owner, Some(random_info.sender.clone())); let mint_msg = ExecuteMsg::Mint { token_id: "randoms_token".to_string(), @@ -278,7 +270,7 @@ fn burning() { let mint_msg = ExecuteMsg::Mint { token_id: token_id.clone(), - owner: MINTER.to_string(), + owner: MINTER_ADDR.to_string(), token_uri: Some(token_uri), extension: None, }; @@ -286,7 +278,7 @@ fn burning() { let burn_msg = ExecuteMsg::Burn { token_id }; // mint some NFT - let allowed = mock_info(MINTER, &[]); + let allowed = mock_info(MINTER_ADDR, &[]); let _ = contract .execute(deps.as_mut(), mock_env(), allowed.clone(), mint_msg) .unwrap(); @@ -333,7 +325,7 @@ fn transferring_nft() { extension: None, }; - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); contract .execute(deps.as_mut(), mock_env(), minter, mint_msg) .unwrap(); @@ -387,7 +379,7 @@ fn sending_nft() { extension: None, }; - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); contract .execute(deps.as_mut(), mock_env(), minter, mint_msg) .unwrap(); @@ -453,7 +445,7 @@ fn approving_revoking() { extension: None, }; - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); contract .execute(deps.as_mut(), mock_env(), minter, mint_msg) .unwrap(); @@ -600,7 +592,7 @@ fn approving_all_revoking_all() { extension: None, }; - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); contract .execute(deps.as_mut(), mock_env(), minter.clone(), mint_msg1) .unwrap(); @@ -861,15 +853,23 @@ fn test_set_withdraw_address() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut()); - // other cant set + // other than creator cant set let err = contract - .set_withdraw_address(deps.as_mut(), &Addr::unchecked("other"), "foo".to_string()) + .set_withdraw_address( + deps.as_mut(), + &Addr::unchecked(MINTER_ADDR), + "foo".to_string(), + ) .unwrap_err(); assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); - // minter can set + // creator can set contract - .set_withdraw_address(deps.as_mut(), &Addr::unchecked(MINTER), "foo".to_string()) + .set_withdraw_address( + deps.as_mut(), + &Addr::unchecked(CREATOR_ADDR), + "foo".to_string(), + ) .unwrap(); let withdraw_address = contract @@ -884,30 +884,38 @@ fn test_remove_withdraw_address() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut()); - // other cant remove + // other than creator cant remove let err = contract - .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked("other")) + .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(MINTER_ADDR)) .unwrap_err(); assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); - // no owner set yet + // no withdraw address set yet let err = contract - .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(MINTER)) + .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(CREATOR_ADDR)) .unwrap_err(); assert_eq!(err, ContractError::NoWithdrawAddress {}); // set and remove contract - .set_withdraw_address(deps.as_mut(), &Addr::unchecked(MINTER), "foo".to_string()) + .set_withdraw_address( + deps.as_mut(), + &Addr::unchecked(CREATOR_ADDR), + "foo".to_string(), + ) .unwrap(); contract - .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(MINTER)) + .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(CREATOR_ADDR)) .unwrap(); assert!(!contract.withdraw_address.exists(deps.as_ref().storage)); // test that we can set again contract - .set_withdraw_address(deps.as_mut(), &Addr::unchecked(MINTER), "foo".to_string()) + .set_withdraw_address( + deps.as_mut(), + &Addr::unchecked(CREATOR_ADDR), + "foo".to_string(), + ) .unwrap(); let withdraw_address = contract .withdraw_address @@ -927,9 +935,13 @@ fn test_withdraw_funds() { .unwrap_err(); assert_eq!(err, ContractError::NoWithdrawAddress {}); - // set and withdraw by non-owner + // set and withdraw by non-creator contract - .set_withdraw_address(deps.as_mut(), &Addr::unchecked(MINTER), "foo".to_string()) + .set_withdraw_address( + deps.as_mut(), + &Addr::unchecked(CREATOR_ADDR), + "foo".to_string(), + ) .unwrap(); contract .withdraw_funds(deps.as_mut().storage, &Coin::new(100, "uark")) @@ -940,7 +952,7 @@ fn test_withdraw_funds() { fn query_tokens_by_owner() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut()); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); // Mint a couple tokens (from the same owner) let token_id1 = "grow1".to_string(); diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index fe3d88140..7ccacfb67 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -1,16 +1,17 @@ -use cw_ownable::OwnershipError; +use cw_ownable::{Action, Ownership, OwnershipError}; use serde::de::DeserializeOwned; use serde::Serialize; use cosmwasm_std::{ - Addr, BankMsg, Binary, Coin, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, Storage, + Addr, Api, BankMsg, Binary, Coin, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, + StdResult, Storage, }; use cw721::{CollectionInfo, Cw721Execute, Cw721ReceiveMsg, Expiration}; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg}; -use crate::state::{Approval, Cw721Contract, NftInfo, CREATOR}; +use crate::state::{Approval, Cw721Contract, NftInfo, CREATOR, MINTER}; impl< 'a, @@ -50,19 +51,23 @@ where }; self.collection_info.save(deps.storage, &collection_info)?; - let minter = match msg.minter { - Some(owner) => deps.api.addr_validate(&owner)?, - None => info.sender.clone(), + // use info.sender if None is passed + let minter: &str = match msg.minter.as_deref() { + Some(minter) => minter, + None => info.sender.as_str(), }; - cw_ownable::initialize_owner(deps.storage, deps.api, Some(minter.as_ref()))?; - let creator = match msg.creator { - Some(owner) => deps.api.addr_validate(&owner)?, - None => info.sender, + self.initialize_minter(deps.storage, deps.api, Some(minter))?; + + // use info.sender if None is passed + let creator: &str = match msg.creator.as_deref() { + Some(creator) => creator, + None => info.sender.as_str(), }; - CREATOR.initialize_owner(deps.storage, deps.api, Some(creator.as_ref()))?; + self.initialize_creator(deps.storage, deps.api, Some(creator))?; - if let Some(address) = msg.withdraw_address { - self.set_withdraw_address(deps, &minter, address)?; + if let Some(withdraw_address) = msg.withdraw_address { + let creator = deps.api.addr_validate(creator)?; + self.set_withdraw_address(deps, &creator, withdraw_address)?; } Ok(Response::default() @@ -106,7 +111,16 @@ where msg, } => self.send_nft(deps, env, info, contract, token_id, msg), ExecuteMsg::Burn { token_id } => self.burn(deps, env, info, token_id), - ExecuteMsg::UpdateOwnership(action) => Self::update_ownership(deps, env, info, action), + #[allow(deprecated)] + ExecuteMsg::UpdateOwnership(action) => { + Self::update_minter_ownership(deps, env, info, action) + } + ExecuteMsg::UpdateMinterOwnership(action) => { + Self::update_minter_ownership(deps, env, info, action) + } + ExecuteMsg::UpdateCreatorOwnership(action) => { + Self::update_creator_ownership(deps, env, info, action) + } ExecuteMsg::Extension { msg: _ } => Ok(Response::default()), ExecuteMsg::SetWithdrawAddress { address } => { self.set_withdraw_address(deps, &info.sender, address) @@ -143,6 +157,24 @@ where TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { + pub fn initialize_creator( + &self, + storage: &mut dyn Storage, + api: &dyn Api, + creator: Option<&str>, + ) -> StdResult> { + CREATOR.initialize_owner(storage, api, creator) + } + + pub fn initialize_minter( + &self, + storage: &mut dyn Storage, + api: &dyn Api, + minter: Option<&str>, + ) -> StdResult> { + MINTER.initialize_owner(storage, api, minter) + } + pub fn mint( &self, deps: DepsMut, @@ -152,7 +184,7 @@ where token_uri: Option, extension: TMetadata, ) -> Result, ContractError> { - cw_ownable::assert_owner(deps.storage, &info.sender)?; + MINTER.assert_owner(deps.storage, &info.sender)?; // create the token let token = NftInfo { @@ -176,14 +208,28 @@ where .add_attribute("token_id", token_id)) } - pub fn update_ownership( + pub fn update_minter_ownership( deps: DepsMut, env: Env, info: MessageInfo, - action: cw_ownable::Action, + action: Action, ) -> Result, ContractError> { - let ownership = cw_ownable::update_ownership(deps, &env.block, &info.sender, action)?; - Ok(Response::new().add_attributes(ownership.into_attributes())) + let ownership = MINTER.update_ownership(deps, &env.block, &info.sender, action)?; + Ok(Response::new() + .add_attribute("update_minter_ownership", info.sender) + .add_attributes(ownership.into_attributes())) + } + + pub fn update_creator_ownership( + deps: DepsMut, + env: Env, + info: MessageInfo, + action: Action, + ) -> Result, ContractError> { + let ownership = CREATOR.update_ownership(deps, &env.block, &info.sender, action)?; + Ok(Response::new() + .add_attribute("update_creator_ownership", info.sender) + .add_attributes(ownership.into_attributes())) } pub fn set_withdraw_address( @@ -192,7 +238,7 @@ where sender: &Addr, address: String, ) -> Result, ContractError> { - cw_ownable::assert_owner(deps.storage, sender)?; + CREATOR.assert_owner(deps.storage, sender)?; deps.api.addr_validate(&address)?; self.withdraw_address.save(deps.storage, &address)?; Ok(Response::new() @@ -205,7 +251,7 @@ where storage: &mut dyn Storage, sender: &Addr, ) -> Result, ContractError> { - cw_ownable::assert_owner(storage, sender)?; + CREATOR.assert_owner(storage, sender)?; let address = self.withdraw_address.may_load(storage)?; match address { Some(address) => { @@ -223,8 +269,8 @@ where storage: &mut dyn Storage, amount: &Coin, ) -> Result, ContractError> { - let address = self.withdraw_address.may_load(storage)?; - match address { + let withdraw_address = self.withdraw_address.may_load(storage)?; + match withdraw_address { Some(address) => { let msg = BankMsg::Send { to_address: address, diff --git a/contracts/cw721-base/src/helpers.rs b/contracts/cw721-base/src/helpers.rs index 2dbb53b94..288ea831d 100644 --- a/contracts/cw721-base/src/helpers.rs +++ b/contracts/cw721-base/src/helpers.rs @@ -134,7 +134,7 @@ impl StdResult> { - let req = QueryMsg::CollectionInfo {}; + let req = QueryMsg::GetCollectionInfo {}; self.query(querier, req) } diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index 9115e0afa..61d0c5a33 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -33,7 +33,10 @@ pub const CONTRACT_NAME: &str = "crates.io:cw721-base"; pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub mod entry { - use self::state::{token_owner_idx, NftInfo, TokenIndexes}; + use self::{ + msg::MigrateMsg, + state::{token_owner_idx, NftInfo, TokenIndexes, CREATOR, MINTER}, + }; use super::*; @@ -43,7 +46,7 @@ pub mod entry { Addr, Api, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult, Storage, }; use cw721::CollectionInfo; - use cw_ownable::OWNERSHIP; + use cw_ownable::none_or; use cw_storage_plus::{IndexedMap, Item, MultiIndex}; // This makes a conscious choice on the various generics used by the contract @@ -84,19 +87,25 @@ pub mod entry { } #[cfg_attr(not(feature = "library"), entry_point)] - pub fn migrate(deps: DepsMut, env: Env, msg: Empty) -> Result { + pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> Result { let response = Response::::default(); - let response = migrate_legacy_minter(deps.storage, deps.api, &env, &msg, response)?; + // first migrate legacy data ... + let response = + migrate_legacy_minter_and_creator(deps.storage, deps.api, &env, &msg, response)?; let response = migrate_legacy_collection_info(deps.storage, &env, &msg, response)?; let response = migrate_legacy_tokens(deps.storage, &env, &msg, response)?; + // ... then migrate let response = migrate_version(deps.storage, &env, &msg, response)?; + // ... and update creator and minter AFTER legacy migration + let response = migrate_creator(deps.storage, deps.api, &env, &msg, response)?; + let response = migrate_minter(deps.storage, deps.api, &env, &msg, response)?; Ok(response) } pub fn migrate_version( storage: &mut dyn Storage, _env: &Env, - _msg: &Empty, + _msg: &MigrateMsg, response: Response, ) -> StdResult { let response = response @@ -108,33 +117,90 @@ pub mod entry { Ok(response) } + pub fn migrate_creator( + storage: &mut dyn Storage, + api: &dyn Api, + _env: &Env, + msg: &MigrateMsg, + response: Response, + ) -> StdResult { + match msg { + MigrateMsg::WithUpdate { creator, .. } => { + if let Some(creator) = creator { + CREATOR.initialize_owner(storage, api, Some(creator.as_str()))?; + return Ok(response.add_attribute("creator", creator)); + } + } + } + Ok(response) + } + + pub fn migrate_minter( + storage: &mut dyn Storage, + api: &dyn Api, + _env: &Env, + msg: &MigrateMsg, + response: Response, + ) -> StdResult { + match msg { + MigrateMsg::WithUpdate { minter, .. } => { + if let Some(minter) = minter { + MINTER.initialize_owner(storage, api, Some(minter.as_str()))?; + return Ok(response.add_attribute("creator", minter)); + } + } + } + Ok(response) + } + /// Migrates only in case ownership is not present - pub fn migrate_legacy_minter( + /// !!! Important note here: !!! + /// - creator owns the contract and can update collection info + /// - minter can mint new tokens + /// + /// Before v0.19.0 there were confusing naming conventions: + /// - v0.17.0: minter was replaced by cw_ownable, as a result minter is owner + /// - v0.16.0 and below: minter was stored in dedicated `minter` store (so NOT using cw_ownable at all) + pub fn migrate_legacy_minter_and_creator( storage: &mut dyn Storage, api: &dyn Api, _env: &Env, - _msg: &Empty, + _msg: &MigrateMsg, response: Response, ) -> Result { - match OWNERSHIP.item.may_load(storage)? { - Some(_) => Ok(response), + let minter = MINTER.item.may_load(storage)?; + // no migration in case minter is already set + if minter.is_some() { + return Ok(response); + } + // in v0.17/18 cw_ownable::OWNERSHIP was used for minter, now it is used for creator + let ownership_previously_used_as_minter = CREATOR.item.may_load(storage)?; + let creator_and_minter = match ownership_previously_used_as_minter { + // v0.18 migration + Some(ownership) => { + // owner is used for both: creator and minter + // since it is already set for creator, we only need to migrate minter + let owner = ownership.owner.map(|a| a.to_string()); + MINTER.initialize_owner(storage, api, owner.as_deref())?; + owner + } + // v0.17 and older migration None => { let legacy_minter_store: Item = Item::new("minter"); let legacy_minter = legacy_minter_store.load(storage)?; - let ownership = - cw_ownable::initialize_owner(storage, api, Some(legacy_minter.as_str()))?; - Ok(response - .add_attribute("old_minter", legacy_minter) - .add_attributes(ownership.into_attributes())) + MINTER.initialize_owner(storage, api, Some(legacy_minter.as_str()))?; + CREATOR.initialize_owner(storage, api, Some(legacy_minter.as_str()))?; + Some(legacy_minter.to_string()) } - } + }; + Ok(response.add_attribute("creator_and_minter", none_or(creator_and_minter.as_ref()))) } /// Migrates only in case collection_info is not present pub fn migrate_legacy_collection_info( storage: &mut dyn Storage, env: &Env, - _msg: &Empty, + _msg: &MigrateMsg, response: Response, ) -> Result { let contract = Cw721Contract::< @@ -169,7 +235,7 @@ pub mod entry { pub fn migrate_legacy_tokens( storage: &mut dyn Storage, _env: &Env, - _msg: &Empty, + _msg: &MigrateMsg, response: Response, ) -> StdResult { let contract = Cw721Contract::::default(); @@ -208,8 +274,9 @@ mod tests { use cw_storage_plus::{IndexedMap, Item, MultiIndex}; use crate::{ + msg::MigrateMsg, query::MAX_LIMIT, - state::{token_owner_idx, NftInfo, TokenIndexes, CREATOR}, + state::{token_owner_idx, NftInfo, TokenIndexes, CREATOR, MINTER}, }; use super::*; @@ -234,12 +301,20 @@ mod tests { ) .unwrap(); - let minter = cw_ownable::get_ownership(deps.as_ref().storage) + let minter = MINTER + .get_ownership(deps.as_ref().storage) .unwrap() .owner .map(|a| a.into_string()); assert_eq!(minter, Some("minter".to_string())); + let creator = CREATOR + .get_ownership(deps.as_ref().storage) + .unwrap() + .owner + .map(|a| a.into_string()); + assert_eq!(creator, Some("creator".to_string())); + let version = cw2::get_contract_version(deps.as_ref().storage).unwrap(); assert_eq!( version, @@ -254,10 +329,11 @@ mod tests { fn proper_owner_initialization() { let mut deps = mock_dependencies(); + let info_owner = mock_info("owner", &[]); entry::instantiate( deps.as_mut(), mock_env(), - mock_info("owner", &[]), + info_owner.clone(), InstantiateMsg { name: "collection_name".into(), symbol: "collection_symbol".into(), @@ -269,11 +345,8 @@ mod tests { ) .unwrap(); - let minter = cw_ownable::get_ownership(deps.as_ref().storage) - .unwrap() - .owner - .map(|a| a.into_string()); - assert_eq!(minter, Some("owner".to_string())); + let minter = MINTER.item.load(deps.as_ref().storage).unwrap().owner; + assert_eq!(minter, Some(info_owner.sender)); let creator = CREATOR.item.load(deps.as_ref().storage).unwrap().owner; assert_eq!(creator, Some(Addr::unchecked("owner"))); } @@ -310,7 +383,7 @@ mod tests { // assert new data before migration: // - ownership and collection info throws NotFound Error - cw_ownable::get_ownership(deps.as_ref().storage).unwrap_err(); + MINTER.item.load(deps.as_ref().storage).unwrap_err(); // cw_ownable in v16 is used for minter let contract = Cw721Contract::< EmptyExtension, Empty, @@ -319,6 +392,9 @@ mod tests { EmptyCollectionInfoExtension, >::default(); contract.collection_info(deps.as_ref()).unwrap_err(); + // query on minter and creator store also throws NotFound Error + MINTER.get_ownership(deps.as_ref().storage).unwrap_err(); + CREATOR.get_ownership(deps.as_ref().storage).unwrap_err(); // - no tokens let all_tokens = contract .all_tokens(deps.as_ref(), None, Some(MAX_LIMIT)) @@ -364,7 +440,15 @@ mod tests { assert_eq!(legacy_token.owner.as_str(), "owner"); } - entry::migrate(deps.as_mut(), env.clone(), Empty {}).unwrap(); + entry::migrate( + deps.as_mut(), + env.clone(), + MigrateMsg::WithUpdate { + minter: None, + creator: None, + }, + ) + .unwrap(); // version let version = cw2::get_contract_version(deps.as_ref().storage) @@ -373,12 +457,21 @@ mod tests { assert_eq!(version, CONTRACT_VERSION); assert_ne!(version, "0.16.0"); - // assert ownership - let ownership = cw_ownable::get_ownership(deps.as_ref().storage) + // assert minter ownership + let minter_ownership = MINTER + .get_ownership(deps.as_ref().storage) + .unwrap() + .owner + .map(|a| a.into_string()); + assert_eq!(minter_ownership, Some("legacy_minter".to_string())); + + // assert creator ownership + let creator_ownership = CREATOR + .get_ownership(deps.as_ref().storage) .unwrap() .owner .map(|a| a.into_string()); - assert_eq!(ownership, Some("legacy_minter".to_string())); + assert_eq!(creator_ownership, Some("legacy_minter".to_string())); // assert collection info let collection_info = contract.collection_info(deps.as_ref()).unwrap(); diff --git a/contracts/cw721-base/src/msg.rs b/contracts/cw721-base/src/msg.rs index 3b29f030e..f06d616be 100644 --- a/contracts/cw721-base/src/msg.rs +++ b/contracts/cw721-base/src/msg.rs @@ -1,7 +1,9 @@ +use std::{os::raw, result}; + use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Binary, Coin, Empty}; +use cosmwasm_std::{Addr, Binary, Coin, Empty}; use cw721::Expiration; -use cw_ownable::{cw_ownable_execute, cw_ownable_query}; +use cw_ownable::{Action, Ownership}; use schemars::JsonSchema; #[cw_serde] @@ -27,11 +29,17 @@ pub struct InstantiateMsg { /// This is like Cw721ExecuteMsg but we add a Mint command for an owner /// to make this stand-alone. You will likely want to remove mint and /// use other control logic in any contract that inherits this. -#[cw_ownable_execute] #[cw_serde] pub enum ExecuteMsg { + #[deprecated(since = "0.19.0", note = "Please use UpdateMinterOwnership instead")] + UpdateOwnership(Action), + UpdateMinterOwnership(Action), + UpdateCreatorOwnership(Action), /// Transfer is a base message to move a token to another account without triggering actions - TransferNft { recipient: String, token_id: String }, + TransferNft { + recipient: String, + token_id: String, + }, /// Send is a base message to transfer a token to a contract and trigger an action /// on the receiving contract. SendNft { @@ -47,7 +55,10 @@ pub enum ExecuteMsg { expires: Option, }, /// Remove previously granted Approval - Revoke { spender: String, token_id: String }, + Revoke { + spender: String, + token_id: String, + }, /// Allows operator to transfer / send any token from the owner's account. /// If expiration is set, then this allowance has a time/height limit ApproveAll { @@ -55,7 +66,9 @@ pub enum ExecuteMsg { expires: Option, }, /// Remove previously granted ApproveAll permission - RevokeAll { operator: String }, + RevokeAll { + operator: String, + }, /// Mint a new NFT, can only be called by the contract minter Mint { @@ -72,21 +85,28 @@ pub enum ExecuteMsg { }, /// Burn an NFT the sender has access to - Burn { token_id: String }, + Burn { + token_id: String, + }, /// Extension msg - Extension { msg: TExtensionExecuteMsg }, + Extension { + msg: TExtensionExecuteMsg, + }, /// Sets address to send withdrawn fees to. Only owner can call this. - SetWithdrawAddress { address: String }, + SetWithdrawAddress { + address: String, + }, /// Removes the withdraw address, so fees are sent to the contract. Only owner can call this. RemoveWithdrawAddress {}, /// Withdraw from the contract to the given address. Anyone can call this, /// which is okay since withdraw address has been set by owner. - WithdrawFunds { amount: Coin }, + WithdrawFunds { + amount: Coin, + }, } -#[cw_ownable_query] #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { @@ -130,14 +150,25 @@ pub enum QueryMsg { #[returns(cw721::NumTokensResponse)] NumTokens {}, - #[deprecated(since = "0.19.0", note = "Please use CollectionInfo instead")] + #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] #[returns(cw721::CollectionInfo)] ContractInfo {}, /// With MetaData Extension. /// Returns top-level metadata about the contract #[returns(cw721::CollectionInfo)] - CollectionInfo {}, + GetCollectionInfo {}, + + #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] + #[returns(Ownership)] + Ownership {}, + + #[returns(Ownership)] + GetMinterOwnership {}, + + #[returns(Ownership)] + GetCreatorOwnership {}, + /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract @@ -170,6 +201,7 @@ pub enum QueryMsg { }, /// Return the minter + #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] #[returns(MinterResponse)] Minter {}, @@ -186,3 +218,11 @@ pub enum QueryMsg { pub struct MinterResponse { pub minter: Option, } + +#[cw_serde] +pub enum MigrateMsg { + WithUpdate { + minter: Option, + creator: Option, + }, +} diff --git a/contracts/cw721-base/src/multi_tests.rs b/contracts/cw721-base/src/multi_tests.rs index d691d8a0a..951d5fe0f 100644 --- a/contracts/cw721-base/src/multi_tests.rs +++ b/contracts/cw721-base/src/multi_tests.rs @@ -1,8 +1,13 @@ use cosmwasm_std::{to_json_binary, Addr, Empty, QuerierWrapper, WasmMsg}; use cw721::OwnerOfResponse; use cw_multi_test::{App, Contract, ContractWrapper, Executor}; +use cw_ownable::{Ownership, OwnershipError}; -use crate::MinterResponse; +use crate::{msg::MigrateMsg, ContractError, MinterResponse}; + +pub const CREATOR_ADDR: &str = "creator"; +pub const MINTER_ADDR: &str = "minter"; +pub const OTHER_ADDR: &str = "other"; fn cw721_base_latest_contract() -> Box> { let contract = ContractWrapper::new( @@ -24,6 +29,26 @@ fn cw721_base_016_contract() -> Box> { Box::new(contract) } +fn cw721_base_017_contract() -> Box> { + use cw721_base_017 as v17; + let contract = ContractWrapper::new( + v17::entry::execute, + v17::entry::instantiate, + v17::entry::query, + ); + Box::new(contract) +} + +fn cw721_base_018_contract() -> Box> { + use cw721_base_018 as v18; + let contract = ContractWrapper::new( + v18::entry::execute, + v18::entry::instantiate, + v18::entry::query, + ); + Box::new(contract) +} + fn query_owner(querier: QuerierWrapper, cw721: &Addr, token_id: String) -> Addr { let resp: OwnerOfResponse = querier .query_wasm_smart( @@ -80,62 +105,638 @@ fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: St /// Instantiates a 0.16 version of this contract and tests that tokens /// can be minted, transferred, and burnred after migration. #[test] -fn test_migration_016_to_latest() { - use cw721_base_016 as v16; - let mut app = App::default(); - let admin = || Addr::unchecked("admin"); +fn test_migration_legacy_to_latest() { + // case 1: migrate from v0.16 to latest by using existing minter addr + { + use cw721_base_016 as v16; + let mut app = App::default(); + let admin = Addr::unchecked("admin"); - let code_id_016 = app.store_code(cw721_base_016_contract()); - let code_id_latest = app.store_code(cw721_base_latest_contract()); + let code_id_016 = app.store_code(cw721_base_016_contract()); + let code_id_latest = app.store_code(cw721_base_latest_contract()); - let cw721 = app - .instantiate_contract( - code_id_016, - admin(), - &v16::InstantiateMsg { - name: "collection".to_string(), - symbol: "symbol".to_string(), - minter: admin().into_string(), - }, - &[], - "cw721-base", - Some(admin().into_string()), + let legacy_creator_and_minter = Addr::unchecked("legacy_creator_and_minter"); + + let cw721 = app + .instantiate_contract( + code_id_016, + legacy_creator_and_minter.clone(), + &v16::InstantiateMsg { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: legacy_creator_and_minter.to_string(), + }, + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); + + mint_transfer_and_burn( + &mut app, + cw721.clone(), + legacy_creator_and_minter.clone(), + "1".to_string(), + ); + + // migrate + app.execute( + admin.clone(), + WasmMsg::Migrate { + contract_addr: cw721.to_string(), + new_code_id: code_id_latest, + msg: to_json_binary(&MigrateMsg::WithUpdate { + minter: None, + creator: None, + }) + .unwrap(), + } + .into(), ) .unwrap(); - mint_transfer_and_burn(&mut app, cw721.clone(), admin(), "1".to_string()); + // non-minter user cant mint + let other = Addr::unchecked(OTHER_ADDR); + let err: ContractError = app + .execute_contract( + other.clone(), + cw721.clone(), + &crate::ExecuteMsg::::Mint { + token_id: "1".to_string(), + owner: other.to_string(), + token_uri: None, + extension: Empty::default(), + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); - app.execute( - admin(), - WasmMsg::Migrate { - contract_addr: cw721.to_string(), - new_code_id: code_id_latest, - msg: to_json_binary(&Empty::default()).unwrap(), - } - .into(), - ) - .unwrap(); + // legacy minter can still mint + mint_transfer_and_burn( + &mut app, + cw721.clone(), + legacy_creator_and_minter.clone(), + "1".to_string(), + ); - mint_transfer_and_burn(&mut app, cw721.clone(), admin(), "1".to_string()); + // check new mint query response works. + let m: MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); - // check new mint query response works. - let m: MinterResponse = app - .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + // check that the new response is backwards compatable when minter + // is not None. + let m: v16::MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, legacy_creator_and_minter.to_string()); + + // check minter ownership query works + let minter_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .unwrap(); + assert_eq!( + minter_ownership.owner, + Some(legacy_creator_and_minter.clone()) + ); + + // check creator ownership query works + let creator_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .unwrap(); + assert_eq!(creator_ownership.owner, Some(legacy_creator_and_minter)); + } + // case 2: migrate from v0.16 to latest by providing new creator and minter addr + { + use cw721_base_016 as v16; + let mut app = App::default(); + let admin = Addr::unchecked("admin"); + + let code_id_016 = app.store_code(cw721_base_016_contract()); + let code_id_latest = app.store_code(cw721_base_latest_contract()); + + let legacy_creator_and_minter = Addr::unchecked("legacy_creator_and_minter"); + + let cw721 = app + .instantiate_contract( + code_id_016, + legacy_creator_and_minter.clone(), + &v16::InstantiateMsg { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: legacy_creator_and_minter.to_string(), + }, + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); + + mint_transfer_and_burn( + &mut app, + cw721.clone(), + legacy_creator_and_minter.clone(), + "1".to_string(), + ); + + // migrate + app.execute( + admin.clone(), + WasmMsg::Migrate { + contract_addr: cw721.to_string(), + new_code_id: code_id_latest, + msg: to_json_binary(&MigrateMsg::WithUpdate { + minter: Some(MINTER_ADDR.to_string()), + creator: Some(CREATOR_ADDR.to_string()), + }) + .unwrap(), + } + .into(), + ) .unwrap(); - assert_eq!(m.minter, Some(admin().to_string())); - // check that the new response is backwards compatable when minter - // is not None. - let m: v16::MinterResponse = app - .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + // legacy minter user cant mint + let err: ContractError = app + .execute_contract( + legacy_creator_and_minter.clone(), + cw721.clone(), + &crate::ExecuteMsg::::Mint { + token_id: "1".to_string(), + owner: legacy_creator_and_minter.to_string(), + token_uri: None, + extension: Empty::default(), + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + + // new minter can mint + let minter = Addr::unchecked(MINTER_ADDR); + mint_transfer_and_burn(&mut app, cw721.clone(), minter.clone(), "1".to_string()); + + // check new mint query response works. + let m: MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, Some(minter.to_string())); + + // check that the new response is backwards compatable when minter + // is not None. + let m: v16::MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, minter.to_string()); + + // check minter ownership query works + let minter_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .unwrap(); + assert_eq!(minter_ownership.owner, Some(minter.clone())); + + // check creator ownership query works + let creator = Addr::unchecked(CREATOR_ADDR); + let creator_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .unwrap(); + assert_eq!(creator_ownership.owner, Some(creator)); + } + // case 3: migrate from v0.17 to latest by using existing minter addr + { + use cw721_base_017 as v17; + let mut app = App::default(); + let admin = Addr::unchecked("admin"); + + let code_id_017 = app.store_code(cw721_base_017_contract()); + let code_id_latest = app.store_code(cw721_base_latest_contract()); + + let legacy_creator_and_minter = Addr::unchecked("legacy_creator_and_minter"); + + let cw721 = app + .instantiate_contract( + code_id_017, + legacy_creator_and_minter.clone(), + &v17::InstantiateMsg { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: legacy_creator_and_minter.to_string(), + }, + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); + + mint_transfer_and_burn( + &mut app, + cw721.clone(), + legacy_creator_and_minter.clone(), + "1".to_string(), + ); + + // migrate + app.execute( + admin.clone(), + WasmMsg::Migrate { + contract_addr: cw721.to_string(), + new_code_id: code_id_latest, + msg: to_json_binary(&MigrateMsg::WithUpdate { + minter: None, + creator: None, + }) + .unwrap(), + } + .into(), + ) + .unwrap(); + + // non-minter user cant mint + let other = Addr::unchecked(OTHER_ADDR); + let err: ContractError = app + .execute_contract( + other.clone(), + cw721.clone(), + &crate::ExecuteMsg::::Mint { + token_id: "1".to_string(), + owner: other.to_string(), + token_uri: None, + extension: Empty::default(), + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + + // legacy minter can still mint + mint_transfer_and_burn( + &mut app, + cw721.clone(), + legacy_creator_and_minter.clone(), + "1".to_string(), + ); + + // check new mint query response works. + let m: MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); + + // check that the new response is backwards compatable when minter + // is not None. + let m: v17::MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); + + // check minter ownership query works + let minter_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .unwrap(); + assert_eq!( + minter_ownership.owner, + Some(legacy_creator_and_minter.clone()) + ); + + // check creator ownership query works + let creator_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .unwrap(); + assert_eq!(creator_ownership.owner, Some(legacy_creator_and_minter)); + } + // case 4: migrate from v0.17 to latest by providing new creator and minter addr + { + use cw721_base_017 as v17; + let mut app = App::default(); + let admin = Addr::unchecked("admin"); + + let code_id_017 = app.store_code(cw721_base_017_contract()); + let code_id_latest = app.store_code(cw721_base_latest_contract()); + + let legacy_creator_and_minter = Addr::unchecked("legacy_creator_and_minter"); + + let cw721 = app + .instantiate_contract( + code_id_017, + legacy_creator_and_minter.clone(), + &v17::InstantiateMsg { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: legacy_creator_and_minter.to_string(), + }, + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); + + mint_transfer_and_burn( + &mut app, + cw721.clone(), + legacy_creator_and_minter.clone(), + "1".to_string(), + ); + + // migrate + app.execute( + admin.clone(), + WasmMsg::Migrate { + contract_addr: cw721.to_string(), + new_code_id: code_id_latest, + msg: to_json_binary(&MigrateMsg::WithUpdate { + minter: Some(MINTER_ADDR.to_string()), + creator: Some(CREATOR_ADDR.to_string()), + }) + .unwrap(), + } + .into(), + ) + .unwrap(); + + // legacy minter user cant mint + let err: ContractError = app + .execute_contract( + legacy_creator_and_minter.clone(), + cw721.clone(), + &crate::ExecuteMsg::::Mint { + token_id: "1".to_string(), + owner: legacy_creator_and_minter.to_string(), + token_uri: None, + extension: Empty::default(), + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + + // new minter can mint + let minter = Addr::unchecked(MINTER_ADDR); + mint_transfer_and_burn(&mut app, cw721.clone(), minter.clone(), "1".to_string()); + + // check new mint query response works. + let m: MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, Some(minter.to_string())); + + // check that the new response is backwards compatable when minter + // is not None. + let m: v17::MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, Some(minter.to_string())); + + // check minter ownership query works + let minter_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .unwrap(); + assert_eq!(minter_ownership.owner, Some(minter.clone())); + + // check creator ownership query works + let creator = Addr::unchecked(CREATOR_ADDR); + let creator_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .unwrap(); + assert_eq!(creator_ownership.owner, Some(creator)); + } + // case 5: migrate from v0.18 to latest by using existing minter addr + { + use cw721_base_018 as v18; + let mut app = App::default(); + let admin = Addr::unchecked("admin"); + + let code_id_018 = app.store_code(cw721_base_018_contract()); + let code_id_latest = app.store_code(cw721_base_latest_contract()); + + let legacy_creator_and_minter = Addr::unchecked("legacy_creator_and_minter"); + + let cw721 = app + .instantiate_contract( + code_id_018, + legacy_creator_and_minter.clone(), + &v18::InstantiateMsg { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: legacy_creator_and_minter.to_string(), + }, + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); + + mint_transfer_and_burn( + &mut app, + cw721.clone(), + legacy_creator_and_minter.clone(), + "1".to_string(), + ); + + // migrate + app.execute( + admin.clone(), + WasmMsg::Migrate { + contract_addr: cw721.to_string(), + new_code_id: code_id_latest, + msg: to_json_binary(&MigrateMsg::WithUpdate { + minter: None, + creator: None, + }) + .unwrap(), + } + .into(), + ) + .unwrap(); + + // non-minter user cant mint + let other = Addr::unchecked(OTHER_ADDR); + let err: ContractError = app + .execute_contract( + other.clone(), + cw721.clone(), + &crate::ExecuteMsg::::Mint { + token_id: "1".to_string(), + owner: other.to_string(), + token_uri: None, + extension: Empty::default(), + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + + // legacy minter can still mint + mint_transfer_and_burn( + &mut app, + cw721.clone(), + legacy_creator_and_minter.clone(), + "1".to_string(), + ); + + // check new mint query response works. + let m: MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); + + // check that the new response is backwards compatable when minter + // is not None. + let m: v18::MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); + + // check minter ownership query works + let minter_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .unwrap(); + assert_eq!( + minter_ownership.owner, + Some(legacy_creator_and_minter.clone()) + ); + + // check creator ownership query works + let creator_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .unwrap(); + assert_eq!(creator_ownership.owner, Some(legacy_creator_and_minter)); + } + // case 4: migrate from v0.18 to latest by providing new creator and minter addr + { + use cw721_base_018 as v18; + let mut app = App::default(); + let admin = Addr::unchecked("admin"); + + let code_id_018 = app.store_code(cw721_base_018_contract()); + let code_id_latest = app.store_code(cw721_base_latest_contract()); + + let legacy_creator_and_minter = Addr::unchecked("legacy_creator_and_minter"); + + let cw721 = app + .instantiate_contract( + code_id_018, + legacy_creator_and_minter.clone(), + &v18::InstantiateMsg { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: legacy_creator_and_minter.to_string(), + }, + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); + + mint_transfer_and_burn( + &mut app, + cw721.clone(), + legacy_creator_and_minter.clone(), + "1".to_string(), + ); + + // migrate + app.execute( + admin.clone(), + WasmMsg::Migrate { + contract_addr: cw721.to_string(), + new_code_id: code_id_latest, + msg: to_json_binary(&MigrateMsg::WithUpdate { + minter: Some(MINTER_ADDR.to_string()), + creator: Some(CREATOR_ADDR.to_string()), + }) + .unwrap(), + } + .into(), + ) .unwrap(); - assert_eq!(m.minter, admin().to_string()); + + // legacy minter user cant mint + let err: ContractError = app + .execute_contract( + legacy_creator_and_minter.clone(), + cw721.clone(), + &crate::ExecuteMsg::::Mint { + token_id: "1".to_string(), + owner: legacy_creator_and_minter.to_string(), + token_uri: None, + extension: Empty::default(), + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + + // new minter can mint + let minter = Addr::unchecked(MINTER_ADDR); + mint_transfer_and_burn(&mut app, cw721.clone(), minter.clone(), "1".to_string()); + + // check new mint query response works. + let m: MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, Some(minter.to_string())); + + // check that the new response is backwards compatable when minter + // is not None. + let m: v18::MinterResponse = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .unwrap(); + assert_eq!(m.minter, Some(minter.to_string())); + + // check minter ownership query works + let minter_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .unwrap(); + assert_eq!(minter_ownership.owner, Some(minter.clone())); + + // check creator ownership query works + let creator = Addr::unchecked(CREATOR_ADDR); + let creator_ownership: Ownership = app + .wrap() + .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .unwrap(); + assert_eq!(creator_ownership.owner, Some(creator)); + } } /// Test backward compatibility using instantiate msg from a 0.16 version on latest contract. -/// This ensures existing 3rd party contracts doesnt need to updated as well. +/// This ensures existing 3rd party contracts doesnt need to update as well. #[test] fn test_instantiate_016_msg() { use cw721_base_016 as v16; diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index 86c6cf9d7..0ef508d79 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -1,8 +1,10 @@ +use cw_ownable::Ownership; use serde::de::DeserializeOwned; use serde::Serialize; use cosmwasm_std::{ to_json_binary, Addr, Binary, BlockInfo, CustomMsg, Deps, Env, Order, StdError, StdResult, + Storage, }; use cw721::{ @@ -14,7 +16,7 @@ use cw_storage_plus::Bound; use cw_utils::maybe_addr; use crate::msg::{MinterResponse, QueryMsg}; -use crate::state::{Approval, Cw721Contract, NftInfo}; +use crate::state::{Approval, Cw721Contract, NftInfo, CREATOR, MINTER}; pub const DEFAULT_LIMIT: u32 = 10; pub const MAX_LIMIT: u32 = 1000; @@ -287,10 +289,11 @@ where msg: QueryMsg, ) -> StdResult { match msg { + #[allow(deprecated)] QueryMsg::Minter {} => to_json_binary(&self.minter(deps)?), #[allow(deprecated)] QueryMsg::ContractInfo {} => to_json_binary(&self.collection_info(deps)?), - QueryMsg::CollectionInfo {} => to_json_binary(&self.collection_info(deps)?), + QueryMsg::GetCollectionInfo {} => to_json_binary(&self.collection_info(deps)?), QueryMsg::NftInfo { token_id } => to_json_binary(&self.nft_info(deps, token_id)?), QueryMsg::OwnerOf { token_id, @@ -363,7 +366,14 @@ where token_id, include_expired.unwrap_or(false), )?), - QueryMsg::Ownership {} => to_json_binary(&Self::ownership(deps)?), + #[allow(deprecated)] + QueryMsg::Ownership {} => to_json_binary(&self.minter_ownership(deps.storage)?), + QueryMsg::GetMinterOwnership {} => { + to_json_binary(&self.minter_ownership(deps.storage)?) + } + QueryMsg::GetCreatorOwnership {} => { + to_json_binary(&self.creator_ownership(deps.storage)?) + } QueryMsg::Extension { msg: _ } => Ok(Binary::default()), QueryMsg::GetWithdrawAddress {} => { to_json_binary(&self.withdraw_address.may_load(deps.storage)?) @@ -371,16 +381,22 @@ where } } + #[deprecated(since = "0.19.0", note = "Please use minter_ownership instead")] pub fn minter(&self, deps: Deps) -> StdResult { - let minter = cw_ownable::get_ownership(deps.storage)? + let minter = MINTER + .get_ownership(deps.storage)? .owner .map(|a| a.into_string()); Ok(MinterResponse { minter }) } - pub fn ownership(deps: Deps) -> StdResult> { - cw_ownable::get_ownership(deps.storage) + pub fn minter_ownership(&self, storage: &dyn Storage) -> StdResult> { + MINTER.get_ownership(storage) + } + + pub fn creator_ownership(&self, storage: &dyn Storage) -> StdResult> { + CREATOR.get_ownership(storage) } } diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 79a70c901..4dc257d38 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -1,4 +1,4 @@ -use cw_ownable::OwnershipStore; +use cw_ownable::{OwnershipStore, OWNERSHIP_KEY}; use schemars::JsonSchema; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -9,7 +9,13 @@ use cosmwasm_std::{Addr, BlockInfo, CustomMsg, StdResult, Storage}; use cw721::{CollectionInfo, Cw721, Expiration}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; -pub const CREATOR: OwnershipStore = OwnershipStore::new("collection_creator"); +/// Creator owns this contract and can update collection info! +/// !!! Important note here: !!! +/// - creator is stored using using cw-ownable's OWNERSHIP singleton, so it is not stored here +/// - in release v0.18.0 it was used for minter (which is confusing), but now it is used for creator +pub const CREATOR: OwnershipStore = OwnershipStore::new(OWNERSHIP_KEY); +/// - minter is stored in the contract storage using cw_ownable::OwnershipStore (same as for OWNERSHIP but with different key) +pub const MINTER: OwnershipStore = OwnershipStore::new("collection_minter"); pub struct Cw721Contract< 'a, diff --git a/contracts/cw721-expiration/src/contract_tests.rs b/contracts/cw721-expiration/src/contract_tests.rs index 5bd570264..10cd8a88e 100644 --- a/contracts/cw721-expiration/src/contract_tests.rs +++ b/contracts/cw721-expiration/src/contract_tests.rs @@ -10,7 +10,7 @@ use cw721::{ Approval, ApprovalResponse, CollectionInfo, Cw721ReceiveMsg, Expiration, NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; -use cw_ownable::OwnershipError; +use cw_ownable::{Action, Ownership, OwnershipError}; use crate::state::Cw721ExpirationContract; use crate::{ @@ -209,7 +209,7 @@ fn test_update_minter() { deps.as_mut(), mock_env(), minter_info.clone(), - ExecuteMsg::UpdateOwnership(cw_ownable::Action::TransferOwnership { + ExecuteMsg::UpdateMinterOwnership(Action::TransferOwnership { new_owner: "random".to_string(), expiry: None, }), @@ -217,25 +217,25 @@ fn test_update_minter() { .unwrap(); // Minter does not change until ownership transfer completes. - let minter: MinterResponse = from_json( + let minter_ownership: Ownership = from_json( contract - .query(deps.as_ref(), mock_env(), QueryMsg::Minter {}) + .query(deps.as_ref(), mock_env(), QueryMsg::GetMinterOwnership {}) .unwrap(), ) .unwrap(); - assert_eq!(minter.minter, Some(MINTER.to_string())); + assert_eq!(minter_ownership.owner, Some(minter_info.sender.clone())); // Pending ownership transfer should be discoverable via query. - let ownership: cw_ownable::Ownership = from_json( + let ownership: Ownership = from_json( contract - .query(deps.as_ref(), mock_env(), QueryMsg::Ownership {}) + .query(deps.as_ref(), mock_env(), QueryMsg::GetMinterOwnership {}) .unwrap(), ) .unwrap(); assert_eq!( ownership, - cw_ownable::Ownership:: { + Ownership:: { owner: Some(Addr::unchecked(MINTER)), pending_owner: Some(Addr::unchecked("random")), pending_expiry: None, @@ -249,18 +249,18 @@ fn test_update_minter() { deps.as_mut(), mock_env(), random_info.clone(), - ExecuteMsg::UpdateOwnership(cw_ownable::Action::AcceptOwnership), + ExecuteMsg::UpdateMinterOwnership(Action::AcceptOwnership), ) .unwrap(); // Minter changes after ownership transfer is accepted. - let minter: MinterResponse = from_json( + let minter_ownership: Ownership = from_json( contract - .query(deps.as_ref(), mock_env(), QueryMsg::Minter {}) + .query(deps.as_ref(), mock_env(), QueryMsg::GetMinterOwnership {}) .unwrap(), ) .unwrap(); - assert_eq!(minter.minter, Some("random".to_string())); + assert_eq!(minter_ownership.owner, Some(random_info.sender.clone())); let mint_msg = ExecuteMsg::Mint { token_id: "randoms_token".to_string(), diff --git a/contracts/cw721-expiration/src/execute.rs b/contracts/cw721-expiration/src/execute.rs index 6c5618218..1d6f1efc0 100644 --- a/contracts/cw721-expiration/src/execute.rs +++ b/contracts/cw721-expiration/src/execute.rs @@ -3,6 +3,7 @@ use cosmwasm_std::{ }; use cw721::{Cw721Execute, EmptyCollectionInfoExtension, Expiration}; use cw721_base::Cw721Contract; +use cw_ownable::Action; use crate::{ error::ContractError, msg::ExecuteMsg, msg::InstantiateMsg, state::Cw721ExpirationContract, @@ -74,7 +75,16 @@ impl<'a> Cw721ExpirationContract<'a> { msg, } => self.send_nft(deps, env, info, contract, token_id, msg), ExecuteMsg::Burn { token_id } => self.burn(deps, env, info, token_id), - ExecuteMsg::UpdateOwnership(action) => Self::update_ownership(deps, env, info, action), + #[allow(deprecated)] + ExecuteMsg::UpdateOwnership(action) => { + Self::update_minter_ownership(deps, env, info, action) + } + ExecuteMsg::UpdateMinterOwnership(action) => { + Self::update_minter_ownership(deps, env, info, action) + } + ExecuteMsg::UpdateCreatorOwnership(action) => { + Self::update_creator_ownership(deps, env, info, action) + } ExecuteMsg::Extension { msg: _ } => Ok(Response::default()), ExecuteMsg::SetWithdrawAddress { address } => { self.set_withdraw_address(deps, &info.sender, address) @@ -109,11 +119,26 @@ impl<'a> Cw721ExpirationContract<'a> { Ok(res) } - pub fn update_ownership( + pub fn update_minter_ownership( + deps: DepsMut, + env: Env, + info: MessageInfo, + action: Action, + ) -> Result { + Ok(Cw721Contract::< + EmptyExtension, + Empty, + Empty, + Empty, + EmptyCollectionInfoExtension, + >::update_minter_ownership(deps, env, info, action)?) + } + + pub fn update_creator_ownership( deps: DepsMut, env: Env, info: MessageInfo, - action: cw_ownable::Action, + action: Action, ) -> Result { Ok(Cw721Contract::< EmptyExtension, @@ -121,7 +146,7 @@ impl<'a> Cw721ExpirationContract<'a> { Empty, Empty, EmptyCollectionInfoExtension, - >::update_ownership(deps, env, info, action)?) + >::update_creator_ownership(deps, env, info, action)?) } pub fn set_withdraw_address( diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index 61d68b5cc..7d892aec8 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -1,7 +1,7 @@ use crate::{EmptyExtension, MinterResponse}; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::Empty; -use cw_ownable::cw_ownable_query; +use cosmwasm_std::{Addr, Empty}; +use cw_ownable::Ownership; pub type ExecuteMsg = cw721_base::ExecuteMsg; #[cw_serde] @@ -28,7 +28,6 @@ pub struct InstantiateMsg { pub withdraw_address: Option, } -#[cw_ownable_query] #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { @@ -78,14 +77,25 @@ pub enum QueryMsg { #[returns(cw721::NumTokensResponse)] NumTokens {}, - #[deprecated(since = "0.19.0", note = "Please use CollectionInfo instead")] + #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] #[returns(cw721::CollectionInfo)] ContractInfo {}, /// With MetaData Extension. /// Returns top-level metadata about the contract #[returns(cw721::CollectionInfo)] - CollectionInfo {}, + GetCollectionInfo {}, + + #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] + #[returns(Ownership)] + Ownership {}, + + #[returns(Ownership)] + GetMinterOwnership {}, + + #[returns(Ownership)] + GetCreatorOwnership {}, + /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract @@ -128,6 +138,7 @@ pub enum QueryMsg { }, /// Return the minter + #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] #[returns(MinterResponse)] Minter {}, diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index 5c30f04f2..442d2c63c 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -1,20 +1,21 @@ -use cosmwasm_std::{to_json_binary, Addr, Binary, Deps, Env, StdResult}; +use cosmwasm_std::{to_json_binary, Addr, Binary, Deps, Env, StdResult, Storage}; use cw721::{ AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721Query, EmptyCollectionInfoExtension, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; use cw721_base::MinterResponse; +use cw_ownable::Ownership; use crate::{error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract, EmptyExtension}; impl<'a> Cw721ExpirationContract<'a> { pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> Result { match msg { - QueryMsg::Minter {} => Ok(to_json_binary(&self.minter(deps)?)?), #[allow(deprecated)] + QueryMsg::Minter {} => Ok(to_json_binary(&self.minter(deps)?)?), QueryMsg::ContractInfo {} => Ok(to_json_binary(&self.collection_info(deps)?)?), - QueryMsg::CollectionInfo {} => Ok(to_json_binary(&self.collection_info(deps)?)?), + QueryMsg::GetCollectionInfo {} => Ok(to_json_binary(&self.collection_info(deps)?)?), QueryMsg::NftInfo { token_id, include_invalid, @@ -119,17 +120,31 @@ impl<'a> Cw721ExpirationContract<'a> { include_expired.unwrap_or(false), include_invalid.unwrap_or(false), )?)?), - QueryMsg::Ownership {} => Ok(to_json_binary(&Self::ownership(deps)?)?), + #[allow(deprecated)] + QueryMsg::Ownership {} => Ok(to_json_binary(&self.minter_ownership(deps.storage)?)?), + QueryMsg::GetMinterOwnership {} => { + Ok(to_json_binary(&self.minter_ownership(deps.storage)?)?) + } + QueryMsg::GetCreatorOwnership {} => { + Ok(to_json_binary(&self.creator_ownership(deps.storage)?)?) + } + QueryMsg::Extension { msg: _ } => Ok(Binary::default()), } } + #[deprecated(since = "0.19.0", note = "Please use minter_ownership instead")] pub fn minter(&self, deps: Deps) -> StdResult { + #[allow(deprecated)] self.base_contract.minter(deps) } - pub fn ownership(deps: Deps) -> StdResult> { - cw_ownable::get_ownership(deps.storage) + pub fn minter_ownership(&self, storage: &dyn Storage) -> StdResult> { + self.base_contract.minter_ownership(storage) + } + + pub fn creator_ownership(&self, storage: &dyn Storage) -> StdResult> { + self.base_contract.creator_ownership(storage) } } diff --git a/contracts/cw721-non-transferable/examples/schema.rs b/contracts/cw721-non-transferable/examples/schema.rs index a388d6536..6c115d7a5 100644 --- a/contracts/cw721-non-transferable/examples/schema.rs +++ b/contracts/cw721-non-transferable/examples/schema.rs @@ -6,9 +6,8 @@ use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, s use cw721::EmptyCollectionInfoExtension; #[allow(deprecated)] use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, - Cw721ExecuteMsg, NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, - TokensResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721ExecuteMsg, + NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; use cw721_non_transferable::{EmptyExtension, InstantiateMsg, MinterResponse, QueryMsg}; diff --git a/contracts/cw721-non-transferable/src/msg.rs b/contracts/cw721-non-transferable/src/msg.rs index 97262a74a..e917c2991 100644 --- a/contracts/cw721-non-transferable/src/msg.rs +++ b/contracts/cw721-non-transferable/src/msg.rs @@ -36,9 +36,12 @@ pub enum QueryMsg { limit: Option, }, NumTokens {}, - #[deprecated(since = "0.19.0", note = "Please use CollectionInfo instead")] + #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] ContractInfo {}, - CollectionInfo {}, + GetCollectionInfo {}, + GetMinterOwnership {}, + GetCreatorOwnership {}, + NftInfo { token_id: String, }, @@ -55,7 +58,10 @@ pub enum QueryMsg { start_after: Option, limit: Option, }, + #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] Minter {}, + + GetWithdrawAddress {}, } impl From for Cw721QueryMsg { @@ -70,8 +76,8 @@ impl From for Cw721QueryMsg { }, QueryMsg::NumTokens {} => Cw721QueryMsg::NumTokens {}, #[allow(deprecated)] - QueryMsg::ContractInfo {} => Cw721QueryMsg::CollectionInfo {}, - QueryMsg::CollectionInfo {} => Cw721QueryMsg::CollectionInfo {}, + QueryMsg::ContractInfo {} => Cw721QueryMsg::GetCollectionInfo {}, + QueryMsg::GetCollectionInfo {} => Cw721QueryMsg::GetCollectionInfo {}, QueryMsg::NftInfo { token_id } => Cw721QueryMsg::NftInfo { token_id }, QueryMsg::AllNftInfo { token_id, @@ -93,7 +99,13 @@ impl From for Cw721QueryMsg { Cw721QueryMsg::AllTokens { start_after, limit } } QueryMsg::Minter {} => Cw721QueryMsg::Minter {}, - _ => unreachable!("cannot convert {:?} to Cw721QueryMsg", msg), + QueryMsg::GetMinterOwnership {} => Cw721QueryMsg::GetMinterOwnership {}, + QueryMsg::GetCreatorOwnership {} => Cw721QueryMsg::GetCreatorOwnership {}, + QueryMsg::GetWithdrawAddress {} => Cw721QueryMsg::GetWithdrawAddress {}, + QueryMsg::AllOperators { .. } => unreachable!("AllOperators is not supported!"), + QueryMsg::Approval { .. } => unreachable!("Approval is not supported!"), + QueryMsg::Approvals { .. } => unreachable!("Approvals is not supported!"), + QueryMsg::Admin { .. } => unreachable!("Approvals is not supported!"), } } } diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index f4775674e..25028c406 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -6,9 +6,9 @@ use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, s use cosmwasm_std::Empty; #[allow(deprecated)] use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, - Cw721ExecuteMsg, Cw721QueryMsg, Cw721ReceiveMsg, NftInfoResponse, NumTokensResponse, - OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721ExecuteMsg, + Cw721QueryMsg, Cw721ReceiveMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, + OperatorsResponse, OwnerOfResponse, TokensResponse, }; use cw721::{EmptyCollectionInfoExtension, EmptyExtension}; fn main() { From 56c6daeb2335d68895ef0ab7b367a22c1fbfe9b0 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 7 Mar 2024 20:03:29 +0100 Subject: [PATCH 16/47] refactor and move key logic to cw721 package: - move logic from cw721-base to cw721 - merge cw721-base and cw721-metadata-onchain into one, distinction is: `extension: T` where T for base contract is `type DefaultMetadataExtension = Option` - all logic now in default implementation for traits Cw721Execute and Cw721Query --- Cargo.lock | 33 +- contracts/cw2981-royalties/examples/schema.rs | 4 +- .../schema/cw2981-royalties.json | 851 ++++++- contracts/cw2981-royalties/src/error.rs | 2 +- contracts/cw2981-royalties/src/lib.rs | 58 +- contracts/cw2981-royalties/src/query.rs | 6 +- contracts/cw721-base/Cargo.toml | 10 - contracts/cw721-base/examples/schema.rs | 12 - contracts/cw721-base/schema/cw721-base.json | 1895 ---------------- contracts/cw721-base/src/error.rs | 29 +- contracts/cw721-base/src/execute.rs | 573 +---- contracts/cw721-base/src/lib.rs | 499 +---- contracts/cw721-base/src/msg.rs | 233 +- contracts/cw721-base/src/query.rs | 405 +--- contracts/cw721-base/src/state.rs | 175 +- contracts/cw721-expiration/examples/schema.rs | 10 +- .../schema/cw721-expiration.json | 1905 +++++++++------- .../cw721-expiration/src/contract_tests.rs | 449 ++-- contracts/cw721-expiration/src/error.rs | 2 +- contracts/cw721-expiration/src/execute.rs | 303 +-- contracts/cw721-expiration/src/lib.rs | 75 +- contracts/cw721-expiration/src/msg.rs | 126 +- contracts/cw721-expiration/src/query.rs | 467 ++-- contracts/cw721-expiration/src/state.rs | 57 +- contracts/cw721-fixed-price/Cargo.toml | 1 + .../cw721-fixed-price/examples/schema.rs | 4 +- .../schema/cw721-fixed-price.json | 260 ++- contracts/cw721-fixed-price/src/contract.rs | 20 +- contracts/cw721-fixed-price/src/msg.rs | 6 +- contracts/cw721-fixed-price/src/state.rs | 4 +- .../cw721-metadata-onchain/.cargo/config | 5 - contracts/cw721-metadata-onchain/Cargo.toml | 31 - contracts/cw721-metadata-onchain/NOTICE | 14 - contracts/cw721-metadata-onchain/README.md | 64 - .../cw721-metadata-onchain/examples/schema.rs | 12 - .../schema/cw721-metadata-onchain.json | 1982 ----------------- contracts/cw721-metadata-onchain/src/lib.rs | 130 -- .../cw721-non-transferable/examples/schema.rs | 36 +- .../schema/all_nft_info_response.json | 105 +- .../schema/approval_response.json | 10 +- .../schema/approvals_response.json | 10 +- .../schema/collection_info.json | 76 +- .../schema/cw721_execute_msg.json | 265 --- .../schema/execute_msg.json | 707 ++++++ .../schema/instantiate_msg.json | 88 +- .../schema/nft_info_response.json | 89 +- .../schema/operators_response.json | 10 +- .../schema/owner_of_response.json | 10 +- .../schema/query_msg.json | 44 +- contracts/cw721-non-transferable/src/lib.rs | 39 +- contracts/cw721-non-transferable/src/msg.rs | 6 +- contracts/cw721-receiver-tester/src/msg.rs | 2 +- .../cw721-receiver-tester/tests/multitest.rs | 13 +- packages/cw721/Cargo.toml | 11 + packages/cw721/README.md | 128 +- packages/cw721/examples/schema.rs | 41 +- .../cw721/schema/all_nft_info_response.json | 105 +- packages/cw721/schema/approval_response.json | 10 +- packages/cw721/schema/approvals_response.json | 10 +- packages/cw721/schema/collection_info.json | 76 +- packages/cw721/schema/cw721_execute_msg.json | 265 --- packages/cw721/schema/cw721_query_msg.json | 315 --- packages/cw721/schema/execute_msg.json | 707 ++++++ packages/cw721/schema/instantiate_msg.json | 136 ++ packages/cw721/schema/nft_info_response.json | 89 +- packages/cw721/schema/operator_response.json | 10 +- packages/cw721/schema/operators_response.json | 10 +- packages/cw721/schema/owner_of_response.json | 10 +- packages/cw721/schema/query_msg.json | 535 +++++ packages/cw721/src/error.rs | 27 + packages/cw721/src/execute.rs | 810 +++++++ .../cw721}/src/helpers.rs | 39 +- packages/cw721/src/lib.rs | 30 +- packages/cw721/src/msg.rs | 228 +- packages/cw721/src/query.rs | 557 ++++- packages/cw721/src/state.rs | 202 +- packages/cw721/src/testing/contract.rs | 115 + .../cw721/src/testing}/contract_tests.rs | 482 +++- packages/cw721/src/testing/mod.rs | 4 + .../cw721/src/testing}/multi_tests.rs | 415 +++- packages/cw721/src/testing/unit_tests.rs | 336 +++ packages/cw721/src/traits.rs | 171 -- 82 files changed, 9107 insertions(+), 8989 deletions(-) delete mode 100644 contracts/cw721-base/examples/schema.rs delete mode 100644 contracts/cw721-base/schema/cw721-base.json delete mode 100644 contracts/cw721-metadata-onchain/.cargo/config delete mode 100644 contracts/cw721-metadata-onchain/Cargo.toml delete mode 100644 contracts/cw721-metadata-onchain/NOTICE delete mode 100644 contracts/cw721-metadata-onchain/README.md delete mode 100644 contracts/cw721-metadata-onchain/examples/schema.rs delete mode 100644 contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json delete mode 100644 contracts/cw721-metadata-onchain/src/lib.rs delete mode 100644 contracts/cw721-non-transferable/schema/cw721_execute_msg.json create mode 100644 contracts/cw721-non-transferable/schema/execute_msg.json delete mode 100644 packages/cw721/schema/cw721_execute_msg.json delete mode 100644 packages/cw721/schema/cw721_query_msg.json create mode 100644 packages/cw721/schema/execute_msg.json create mode 100644 packages/cw721/schema/instantiate_msg.json create mode 100644 packages/cw721/schema/query_msg.json create mode 100644 packages/cw721/src/error.rs create mode 100644 packages/cw721/src/execute.rs rename {contracts/cw721-base => packages/cw721}/src/helpers.rs (83%) create mode 100644 packages/cw721/src/testing/contract.rs rename {contracts/cw721-base/src => packages/cw721/src/testing}/contract_tests.rs (64%) create mode 100644 packages/cw721/src/testing/mod.rs rename {contracts/cw721-base/src => packages/cw721/src/testing}/multi_tests.rs (64%) create mode 100644 packages/cw721/src/testing/unit_tests.rs delete mode 100644 packages/cw721/src/traits.rs diff --git a/Cargo.lock b/Cargo.lock index 43e11131a..5289c452f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -453,9 +453,18 @@ version = "0.19.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", + "cw-multi-test", + "cw-ownable 0.6.0", + "cw-storage-plus 1.2.0", "cw-utils 1.0.3", + "cw2 1.1.2", + "cw721 0.16.0 (git+https://github.com/CosmWasm/cw-nfts?tag=v0.16.0)", + "cw721-base 0.16.0 (git+https://github.com/CosmWasm/cw-nfts?tag=v0.16.0)", + "cw721-base 0.17.0", + "cw721-base 0.18.0", "schemars", "serde", + "thiserror", ] [[package]] @@ -531,23 +540,13 @@ dependencies = [ name = "cw721-base" version = "0.19.0" dependencies = [ - "cosmwasm-schema", "cosmwasm-std", - "cw-multi-test", "cw-ownable 0.6.0", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", "cw2 1.1.2", - "cw721 0.16.0 (git+https://github.com/CosmWasm/cw-nfts?tag=v0.16.0)", "cw721 0.17.0", "cw721 0.18.0", "cw721 0.19.0", - "cw721-base 0.16.0 (git+https://github.com/CosmWasm/cw-nfts?tag=v0.16.0)", - "cw721-base 0.17.0", - "cw721-base 0.18.0", - "schemars", "serde", - "thiserror", ] [[package]] @@ -577,6 +576,7 @@ dependencies = [ "cw-utils 1.0.3", "cw2 1.1.2", "cw20", + "cw721 0.19.0", "cw721-base 0.19.0", "prost 0.10.4", "schemars", @@ -584,19 +584,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cw721-metadata-onchain" -version = "0.19.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cw2 1.1.2", - "cw721 0.19.0", - "cw721-base 0.19.0", - "schemars", - "serde", -] - [[package]] name = "cw721-non-transferable" version = "0.19.0" diff --git a/contracts/cw2981-royalties/examples/schema.rs b/contracts/cw2981-royalties/examples/schema.rs index 3ff748de6..0400900bc 100644 --- a/contracts/cw2981-royalties/examples/schema.rs +++ b/contracts/cw2981-royalties/examples/schema.rs @@ -1,11 +1,11 @@ use cosmwasm_schema::write_api; use cw2981_royalties::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use cw721::EmptyCollectionInfoExtension; +use cw721::state::DefaultOptionCollectionInfoExtension; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: InstantiateMsg, execute: ExecuteMsg, query: QueryMsg, } diff --git a/contracts/cw2981-royalties/schema/cw2981-royalties.json b/contracts/cw2981-royalties/schema/cw2981-royalties.json index b0e5a4b1b..b397a12d3 100644 --- a/contracts/cw2981-royalties/schema/cw2981-royalties.json +++ b/contracts/cw2981-royalties/schema/cw2981-royalties.json @@ -14,7 +14,7 @@ "collection_info_extension": { "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" }, { "type": "null" @@ -52,17 +52,155 @@ }, "additionalProperties": false, "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" } } }, "execute": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ExecuteMsg", - "description": "This is like Cw721ExecuteMsg but we add a Mint command for an owner to make this stand-alone. You will likely want to remove mint and use other control logic in any contract that inherits this.", "oneOf": [ + { + "deprecated": true, + "type": "object", + "required": [ + "update_ownership" + ], + "properties": { + "update_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_minter_ownership" + ], + "properties": { + "update_minter_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_creator_ownership" + ], + "properties": { + "update_creator_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "description": "Update the collection, can only called by the contract creator", + "type": "object", + "required": [ + "update_collection_info" + ], + "properties": { + "update_collection_info": { + "type": "object", + "required": [ + "collection_info" + ], + "properties": { + "collection_info": { + "$ref": "#/definitions/CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "Transfer is a base message to move a token to another account without triggering actions", "type": "object", @@ -382,19 +520,6 @@ } }, "additionalProperties": false - }, - { - "description": "Update the contract's ownership. The `action` to be provided can be either to propose transferring ownership to an account, accept a pending ownership transfer, or renounce the ownership permanently.", - "type": "object", - "required": [ - "update_ownership" - ], - "properties": { - "update_ownership": { - "$ref": "#/definitions/Action" - } - }, - "additionalProperties": false } ], "definitions": { @@ -449,6 +574,10 @@ } ] }, + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Binary": { "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" @@ -468,6 +597,84 @@ } } }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "name", + "symbol" + ], + "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, "Empty": { "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", "type": "object" @@ -598,6 +805,22 @@ }, "additionalProperties": false }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, "Timestamp": { "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", "allOf": [ @@ -838,10 +1061,50 @@ "description": "With MetaData Extension. Returns top-level metadata about the contract", "type": "object", "required": [ - "collection_info" + "get_collection_info" + ], + "properties": { + "get_collection_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "deprecated": true, + "type": "object", + "required": [ + "ownership" + ], + "properties": { + "ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_minter_ownership" + ], + "properties": { + "get_minter_ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_creator_ownership" ], "properties": { - "collection_info": { + "get_creator_ownership": { "type": "object", "additionalProperties": false } @@ -967,6 +1230,7 @@ }, { "description": "Return the minter", + "deprecated": true, "type": "object", "required": [ "minter" @@ -1002,26 +1266,38 @@ "additionalProperties": false }, { + "description": "This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg`", "type": "object", "required": [ - "get_withdraw_address" + "get_collection_info_extension" ], "properties": { - "get_withdraw_address": { + "get_collection_info_extension": { "type": "object", + "properties": { + "msg": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + } + }, "additionalProperties": false } }, "additionalProperties": false }, { - "description": "Query the contract's ownership information", "type": "object", "required": [ - "ownership" + "get_withdraw_address" ], "properties": { - "ownership": { + "get_withdraw_address": { "type": "object", "additionalProperties": false } @@ -1030,28 +1306,80 @@ } ], "definitions": { - "Cw2981QueryMsg": { - "oneOf": [ - { - "description": "Should be called on sale to see if royalties are owed by the marketplace selling the NFT, if CheckRoyalties returns true See https://eips.ethereum.org/EIPS/eip-2981", - "type": "object", - "required": [ - "royalty_info" - ], - "properties": { - "royalty_info": { - "type": "object", - "required": [ - "sale_price", - "token_id" - ], - "properties": { - "sale_price": { - "$ref": "#/definitions/Uint128" - }, - "token_id": { - "type": "string" - } + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Cw2981QueryMsg": { + "oneOf": [ + { + "description": "Should be called on sale to see if royalties are owed by the marketplace selling the NFT, if CheckRoyalties returns true See https://eips.ethereum.org/EIPS/eip-2981", + "type": "object", + "required": [ + "royalty_info" + ], + "properties": { + "royalty_info": { + "type": "object", + "required": [ + "sale_price", + "token_id" + ], + "properties": { + "sale_price": { + "$ref": "#/definitions/Uint128" + }, + "token_id": { + "type": "string" + } }, "additionalProperties": false } @@ -1074,9 +1402,41 @@ } ] }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" } } }, @@ -1111,6 +1471,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -1128,7 +1492,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false @@ -1304,6 +1672,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -1321,7 +1693,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false @@ -1419,6 +1795,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -1436,7 +1816,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false @@ -1519,6 +1903,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -1536,7 +1924,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false @@ -1602,7 +1994,7 @@ } } }, - "collection_info": { + "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "CollectionInfo_for_Empty", "type": "object", @@ -1646,19 +2038,30 @@ } } }, - "contract_info": { + "extension": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Empty", + "title": "Null", + "type": "null" + }, + "get_collection_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfo_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo", "type": "object", "required": [ - "extension", "name", "symbol", "updated_at" ], "properties": { "extension": { - "$ref": "#/definitions/Empty" + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] }, "name": { "type": "string" @@ -1672,9 +2075,77 @@ }, "additionalProperties": false, "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false }, "Timestamp": { "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", @@ -1690,11 +2161,225 @@ } } }, - "extension": { + "get_collection_info_extension": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Null", "type": "null" }, + "get_creator_ownership": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Ownership_for_Addr", + "description": "The contract's ownership info", + "type": "object", + "properties": { + "owner": { + "description": "The contract's current owner. `None` if the ownership has been renounced.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "pending_expiry": { + "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "pending_owner": { + "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "get_minter_ownership": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Ownership_for_Addr", + "description": "The contract's ownership info", + "type": "object", + "properties": { + "owner": { + "description": "The contract's current owner. `None` if the ownership has been renounced.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "pending_expiry": { + "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "pending_owner": { + "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, "get_withdraw_address": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Nullable_String", @@ -1824,6 +2509,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -1841,7 +2530,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false @@ -1930,6 +2623,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -1947,7 +2644,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false @@ -2015,15 +2716,19 @@ }, "ownership": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Ownership_for_String", + "title": "Ownership_for_Addr", "description": "The contract's ownership info", "type": "object", "properties": { "owner": { "description": "The contract's current owner. `None` if the ownership has been renounced.", - "type": [ - "string", - "null" + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } ] }, "pending_expiry": { @@ -2039,14 +2744,22 @@ }, "pending_owner": { "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", - "type": [ - "string", - "null" + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } ] } }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", "oneOf": [ diff --git a/contracts/cw2981-royalties/src/error.rs b/contracts/cw2981-royalties/src/error.rs index 73c0e4bb4..2d1cd6324 100644 --- a/contracts/cw2981-royalties/src/error.rs +++ b/contracts/cw2981-royalties/src/error.rs @@ -7,7 +7,7 @@ pub enum ContractError { Std(#[from] StdError), #[error(transparent)] - Base(#[from] cw721_base::ContractError), + Base(#[from] cw721_base::error::ContractError), #[error("Royalty percentage must be between 0 and 100")] InvalidRoyaltyPercentage, diff --git a/contracts/cw2981-royalties/src/lib.rs b/contracts/cw2981-royalties/src/lib.rs index f5c72d139..8a18cb4a7 100644 --- a/contracts/cw2981-royalties/src/lib.rs +++ b/contracts/cw2981-royalties/src/lib.rs @@ -2,13 +2,14 @@ pub mod error; pub mod msg; pub mod query; -use cw721::EmptyCollectionInfoExtension; +use cw721::state::DefaultOptionCollectionInfoExtension; pub use query::{check_royalties, query_royalties_info}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{to_json_binary, Empty}; -use cw721_base::Cw721Contract; -pub use cw721_base::{InstantiateMsg, MinterResponse}; +pub use cw721_base::{ + execute::Cw721Execute, msg::InstantiateMsg, query::Cw721Query, Cw721Contract, +}; use crate::error::ContractError; use crate::msg::Cw2981QueryMsg; @@ -50,10 +51,17 @@ pub type Extension = Option; pub type MintExtension = Option; -pub type Cw2981Contract<'a> = - Cw721Contract<'a, Extension, Empty, Empty, Cw2981QueryMsg, EmptyCollectionInfoExtension>; -pub type ExecuteMsg = cw721_base::ExecuteMsg; -pub type QueryMsg = cw721_base::QueryMsg; +pub type Cw2981Contract<'a> = Cw721Contract< + 'a, + Extension, + Empty, + Empty, + Cw2981QueryMsg, + DefaultOptionCollectionInfoExtension, +>; +pub type ExecuteMsg = + cw721_base::msg::ExecuteMsg; +pub type QueryMsg = cw721_base::msg::QueryMsg; #[cfg(not(feature = "library"))] pub mod entry { @@ -67,11 +75,16 @@ pub mod entry { mut deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - Ok(Cw2981Contract::default().instantiate(deps.branch(), env, info, msg)?) + Ok(Cw2981Contract::default().instantiate( + deps.branch(), + env, + info, + msg, + CONTRACT_NAME, + CONTRACT_VERSION, + )?) } #[entry_point] @@ -109,7 +122,7 @@ pub mod entry { Cw2981QueryMsg::RoyaltyInfo { token_id, sale_price, - } => to_json_binary(&query_royalties_info(deps, token_id, sale_price)?), + } => to_json_binary(&query_royalties_info(deps, env, token_id, sale_price)?), Cw2981QueryMsg::CheckRoyalties {} => to_json_binary(&check_royalties(deps)?), }, _ => Cw2981Contract::default().query(deps, env, msg), @@ -125,7 +138,6 @@ mod tests { use cosmwasm_std::{from_json, Uint128}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cw721::Cw721Query; const CREATOR: &str = "creator"; @@ -158,9 +170,12 @@ mod tests { token_uri: token_uri.clone(), extension: extension.clone(), }; - entry::execute(deps.as_mut(), mock_env(), info, exec_msg).unwrap(); + let env = mock_env(); + entry::execute(deps.as_mut(), env.clone(), info, exec_msg).unwrap(); - let res = contract.nft_info(deps.as_ref(), token_id.into()).unwrap(); + let res = contract + .query_nft_info(deps.as_ref(), env, token_id.into()) + .unwrap(); assert_eq!(res.token_uri, token_uri); assert_eq!(res.extension, extension); } @@ -255,7 +270,8 @@ mod tests { creator: None, withdraw_address: None, }; - entry::instantiate(deps.as_mut(), mock_env(), info.clone(), init_msg).unwrap(); + let env = mock_env(); + entry::instantiate(deps.as_mut(), env.clone(), info.clone(), init_msg).unwrap(); let token_id = "Enterprise"; let owner = "jeanluc"; @@ -277,8 +293,13 @@ mod tests { address: owner.into(), royalty_amount: Uint128::new(10), }; - let res = - query_royalties_info(deps.as_ref(), token_id.to_string(), Uint128::new(100)).unwrap(); + let res = query_royalties_info( + deps.as_ref(), + env.clone(), + token_id.to_string(), + Uint128::new(100), + ) + .unwrap(); assert_eq!(res, expected); // also check the longhand way @@ -319,6 +340,7 @@ mod tests { let res = query_royalties_info( deps.as_ref(), + env, voyager_token_id.to_string(), Uint128::new(43), ) diff --git a/contracts/cw2981-royalties/src/query.rs b/contracts/cw2981-royalties/src/query.rs index 6028b3b2d..373048c89 100644 --- a/contracts/cw2981-royalties/src/query.rs +++ b/contracts/cw2981-royalties/src/query.rs @@ -1,16 +1,18 @@ use crate::msg::{CheckRoyaltiesResponse, RoyaltiesInfoResponse}; use crate::Cw2981Contract; -use cosmwasm_std::{Decimal, Deps, StdResult, Uint128}; +use cosmwasm_std::{Decimal, Deps, Env, StdResult, Uint128}; +use cw721_base::query::Cw721Query; /// NOTE: default behaviour here is to round down /// EIP2981 specifies that the rounding behaviour is at the discretion of the implementer pub fn query_royalties_info( deps: Deps, + env: Env, token_id: String, sale_price: Uint128, ) -> StdResult { let contract = Cw2981Contract::default(); - let token_info = contract.nft_info.load(deps.storage, &token_id)?; + let token_info = contract.query_nft_info(deps, env, token_id)?; let royalty_percentage = match token_info.extension { Some(ref ext) => match ext.royalty_percentage { diff --git a/contracts/cw721-base/Cargo.toml b/contracts/cw721-base/Cargo.toml index 562e52b5d..3ba9c18c2 100644 --- a/contracts/cw721-base/Cargo.toml +++ b/contracts/cw721-base/Cargo.toml @@ -24,22 +24,12 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] -cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-ownable = { workspace = true } -cw-storage-plus = { workspace = true } -cw-utils = { workspace = true } cw2 = { workspace = true } cw721 = { workspace = true } -cw721-016 = { workspace = true } -schemars = { workspace = true } serde = { workspace = true } -thiserror = { workspace = true } [dev-dependencies] -cw-multi-test = { workspace = true } cw721-017 = { workspace = true } cw721-018 = { workspace = true } -cw721-base-016 = { workspace = true, features = ["library"] } -cw721-base-017 = { workspace = true, features = ["library"] } -cw721-base-018 = { workspace = true, features = ["library"] } diff --git a/contracts/cw721-base/examples/schema.rs b/contracts/cw721-base/examples/schema.rs deleted file mode 100644 index 13736af0a..000000000 --- a/contracts/cw721-base/examples/schema.rs +++ /dev/null @@ -1,12 +0,0 @@ -use cosmwasm_schema::write_api; -use cosmwasm_std::Empty; - -use cw721_base::{EmptyCollectionInfoExtension, ExecuteMsg, InstantiateMsg, QueryMsg}; - -fn main() { - write_api! { - instantiate: InstantiateMsg, - execute: ExecuteMsg, - query: QueryMsg, - } -} diff --git a/contracts/cw721-base/schema/cw721-base.json b/contracts/cw721-base/schema/cw721-base.json deleted file mode 100644 index 9573ac851..000000000 --- a/contracts/cw721-base/schema/cw721-base.json +++ /dev/null @@ -1,1895 +0,0 @@ -{ - "contract_name": "cw721-base", - "contract_version": "0.19.0", - "idl_version": "1.0.0", - "instantiate": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "InstantiateMsg", - "type": "object", - "required": [ - "name", - "symbol" - ], - "properties": { - "collection_info_extension": { - "anyOf": [ - { - "$ref": "#/definitions/Empty" - }, - { - "type": "null" - } - ] - }, - "creator": { - "description": "The creator is the only who can update collection info.", - "type": [ - "string", - "null" - ] - }, - "minter": { - "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", - "type": [ - "string", - "null" - ] - }, - "name": { - "description": "Name of the NFT contract", - "type": "string" - }, - "symbol": { - "description": "Symbol of the NFT contract", - "type": "string" - }, - "withdraw_address": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - } - } - }, - "execute": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ExecuteMsg", - "description": "This is like Cw721ExecuteMsg but we add a Mint command for an owner to make this stand-alone. You will likely want to remove mint and use other control logic in any contract that inherits this.", - "oneOf": [ - { - "description": "Transfer is a base message to move a token to another account without triggering actions", - "type": "object", - "required": [ - "transfer_nft" - ], - "properties": { - "transfer_nft": { - "type": "object", - "required": [ - "recipient", - "token_id" - ], - "properties": { - "recipient": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Send is a base message to transfer a token to a contract and trigger an action on the receiving contract.", - "type": "object", - "required": [ - "send_nft" - ], - "properties": { - "send_nft": { - "type": "object", - "required": [ - "contract", - "msg", - "token_id" - ], - "properties": { - "contract": { - "type": "string" - }, - "msg": { - "$ref": "#/definitions/Binary" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Allows operator to transfer / send the token from the owner's account. If expiration is set, then this allowance has a time/height limit", - "type": "object", - "required": [ - "approve" - ], - "properties": { - "approve": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "expires": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Remove previously granted Approval", - "type": "object", - "required": [ - "revoke" - ], - "properties": { - "revoke": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Allows operator to transfer / send any token from the owner's account. If expiration is set, then this allowance has a time/height limit", - "type": "object", - "required": [ - "approve_all" - ], - "properties": { - "approve_all": { - "type": "object", - "required": [ - "operator" - ], - "properties": { - "expires": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "operator": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Remove previously granted ApproveAll permission", - "type": "object", - "required": [ - "revoke_all" - ], - "properties": { - "revoke_all": { - "type": "object", - "required": [ - "operator" - ], - "properties": { - "operator": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Mint a new NFT, can only be called by the contract minter", - "type": "object", - "required": [ - "mint" - ], - "properties": { - "mint": { - "type": "object", - "required": [ - "extension", - "owner", - "token_id" - ], - "properties": { - "extension": { - "description": "Any custom extension used by this contract", - "allOf": [ - { - "$ref": "#/definitions/Empty" - } - ] - }, - "owner": { - "description": "The owner of the newly minter NFT", - "type": "string" - }, - "token_id": { - "description": "Unique ID of the NFT", - "type": "string" - }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Burn an NFT the sender has access to", - "type": "object", - "required": [ - "burn" - ], - "properties": { - "burn": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Extension msg", - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "type": "object", - "required": [ - "msg" - ], - "properties": { - "msg": { - "$ref": "#/definitions/Empty" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Sets address to send withdrawn fees to. Only owner can call this.", - "type": "object", - "required": [ - "set_withdraw_address" - ], - "properties": { - "set_withdraw_address": { - "type": "object", - "required": [ - "address" - ], - "properties": { - "address": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Removes the withdraw address, so fees are sent to the contract. Only owner can call this.", - "type": "object", - "required": [ - "remove_withdraw_address" - ], - "properties": { - "remove_withdraw_address": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Withdraw from the contract to the given address. Anyone can call this, which is okay since withdraw address has been set by owner.", - "type": "object", - "required": [ - "withdraw_funds" - ], - "properties": { - "withdraw_funds": { - "type": "object", - "required": [ - "amount" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Coin" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Update the contract's ownership. The `action` to be provided can be either to propose transferring ownership to an account, accept a pending ownership transfer, or renounce the ownership permanently.", - "type": "object", - "required": [ - "update_ownership" - ], - "properties": { - "update_ownership": { - "$ref": "#/definitions/Action" - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Action": { - "description": "Actions that can be taken to alter the contract's ownership", - "oneOf": [ - { - "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", - "type": "object", - "required": [ - "transfer_ownership" - ], - "properties": { - "transfer_ownership": { - "type": "object", - "required": [ - "new_owner" - ], - "properties": { - "expiry": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "new_owner": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", - "type": "string", - "enum": [ - "accept_ownership" - ] - }, - { - "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", - "type": "string", - "enum": [ - "renounce_ownership" - ] - } - ] - }, - "Binary": { - "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", - "type": "string" - }, - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "query": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", - "oneOf": [ - { - "description": "Return the owner of the given token, error if token does not exist", - "type": "object", - "required": [ - "owner_of" - ], - "properties": { - "owner_of": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return operator that can access all of the owner's tokens.", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return approvals that a token has", - "type": "object", - "required": [ - "approvals" - ], - "properties": { - "approvals": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return approval of a given operator for all tokens of an owner, error if not set", - "type": "object", - "required": [ - "operator" - ], - "properties": { - "operator": { - "type": "object", - "required": [ - "operator", - "owner" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "operator": { - "type": "string" - }, - "owner": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "List all operators that can access all of the owner's tokens", - "type": "object", - "required": [ - "all_operators" - ], - "properties": { - "all_operators": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired items, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Total number of tokens issued", - "type": "object", - "required": [ - "num_tokens" - ], - "properties": { - "num_tokens": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "deprecated": true, - "type": "object", - "required": [ - "contract_info" - ], - "properties": { - "contract_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns top-level metadata about the contract", - "type": "object", - "required": [ - "collection_info" - ], - "properties": { - "collection_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", - "type": "object", - "required": [ - "nft_info" - ], - "properties": { - "nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization for clients", - "type": "object", - "required": [ - "all_nft_info" - ], - "properties": { - "all_nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With Enumerable extension. Requires pagination. Lists all token_ids controlled by the contract.", - "type": "object", - "required": [ - "all_tokens" - ], - "properties": { - "all_tokens": { - "type": "object", - "properties": { - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return the minter", - "type": "object", - "required": [ - "minter" - ], - "properties": { - "minter": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Extension query", - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "type": "object", - "required": [ - "msg" - ], - "properties": { - "msg": { - "$ref": "#/definitions/Empty" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_withdraw_address" - ], - "properties": { - "get_withdraw_address": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Query the contract's ownership information", - "type": "object", - "required": [ - "ownership" - ], - "properties": { - "ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - } - } - }, - "migrate": null, - "sudo": null, - "responses": { - "all_nft_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AllNftInfoResponse_for_Empty", - "type": "object", - "required": [ - "access", - "info" - ], - "properties": { - "access": { - "description": "Who can transfer the token", - "allOf": [ - { - "$ref": "#/definitions/OwnerOfResponse" - } - ] - }, - "info": { - "description": "Data on the token itself,", - "allOf": [ - { - "$ref": "#/definitions/NftInfoResponse_for_Empty" - } - ] - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "NftInfoResponse_for_Empty": { - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "description": "You can add any custom metadata here when you extend cw721-base", - "allOf": [ - { - "$ref": "#/definitions/Empty" - } - ] - }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "OwnerOfResponse": { - "type": "object", - "required": [ - "approvals", - "owner" - ], - "properties": { - "approvals": { - "description": "If set this address is approved to transfer/send the token as well", - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - }, - "owner": { - "description": "Owner of the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "all_operators": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OperatorsResponse", - "type": "object", - "required": [ - "operators" - ], - "properties": { - "operators": { - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "all_tokens": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "TokensResponse", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "description": "Contains all token_ids in lexicographical ordering If there are more than `limit`, use `start_after` in future queries to achieve pagination.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - }, - "approval": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ApprovalResponse", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "$ref": "#/definitions/Approval" - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "approvals": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ApprovalsResponse", - "type": "object", - "required": [ - "approvals" - ], - "properties": { - "approvals": { - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "collection_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Empty", - "type": "object", - "required": [ - "extension", - "name", - "symbol", - "updated_at" - ], - "properties": { - "extension": { - "$ref": "#/definitions/Empty" - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "contract_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Empty", - "type": "object", - "required": [ - "extension", - "name", - "symbol", - "updated_at" - ], - "properties": { - "extension": { - "$ref": "#/definitions/Empty" - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "extension": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Null", - "type": "null" - }, - "get_withdraw_address": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Nullable_String", - "type": [ - "string", - "null" - ] - }, - "minter": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "MinterResponse", - "description": "Shows who can mint these tokens", - "type": "object", - "properties": { - "minter": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "nft_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NftInfoResponse_for_Empty", - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "description": "You can add any custom metadata here when you extend cw721-base", - "allOf": [ - { - "$ref": "#/definitions/Empty" - } - ] - }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - } - } - }, - "num_tokens": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NumTokensResponse", - "type": "object", - "required": [ - "count" - ], - "properties": { - "count": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "operator": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OperatorResponse", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "$ref": "#/definitions/Approval" - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "owner_of": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OwnerOfResponse", - "type": "object", - "required": [ - "approvals", - "owner" - ], - "properties": { - "approvals": { - "description": "If set this address is approved to transfer/send the token as well", - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - }, - "owner": { - "description": "Owner of the token", - "type": "string" - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "ownership": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Ownership_for_String", - "description": "The contract's ownership info", - "type": "object", - "properties": { - "owner": { - "description": "The contract's current owner. `None` if the ownership has been renounced.", - "type": [ - "string", - "null" - ] - }, - "pending_expiry": { - "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "pending_owner": { - "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false, - "definitions": { - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "tokens": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "TokensResponse", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "description": "Contains all token_ids in lexicographical ordering If there are more than `limit`, use `start_after` in future queries to achieve pagination.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - } - } -} diff --git a/contracts/cw721-base/src/error.rs b/contracts/cw721-base/src/error.rs index 8ce92f08c..302102d4b 100644 --- a/contracts/cw721-base/src/error.rs +++ b/contracts/cw721-base/src/error.rs @@ -1,27 +1,2 @@ -use cosmwasm_std::StdError; -use cw_ownable::OwnershipError; -use thiserror::Error; - -#[derive(Error, Debug, PartialEq)] -pub enum ContractError { - #[error(transparent)] - Std(#[from] StdError), - - #[error(transparent)] - Ownership(#[from] OwnershipError), - - #[error(transparent)] - Version(#[from] cw2::VersionError), - - #[error("token_id already claimed")] - Claimed {}, - - #[error("Cannot set approval that is already expired")] - Expired {}, - - #[error("Approval not found for: {spender}")] - ApprovalNotFound { spender: String }, - - #[error("No withdraw address set")] - NoWithdrawAddress {}, -} +// expose to all others using contract, so others dont need to import cw721 +pub use cw721::error::{Cw721ContractError as ContractError, *}; diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 7ccacfb67..406daba26 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -1,139 +1,11 @@ -use cw_ownable::{Action, Ownership, OwnershipError}; +use cosmwasm_std::CustomMsg; +// expose to all others using contract, so others dont need to import cw721 +pub use cw721::execute::*; use serde::de::DeserializeOwned; use serde::Serialize; -use cosmwasm_std::{ - Addr, Api, BankMsg, Binary, Coin, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, - StdResult, Storage, -}; +use crate::Cw721Contract; -use cw721::{CollectionInfo, Cw721Execute, Cw721ReceiveMsg, Expiration}; - -use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InstantiateMsg}; -use crate::state::{Approval, Cw721Contract, NftInfo, CREATOR, MINTER}; - -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > - Cw721Contract< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > -where - TMetadata: Serialize + DeserializeOwned + Clone, - TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, -{ - pub fn instantiate( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: InstantiateMsg, - ) -> Result, ContractError> { - let collection_info = CollectionInfo { - name: msg.name, - symbol: msg.symbol, - extension: msg.collection_info_extension, - updated_at: env.block.time, - }; - self.collection_info.save(deps.storage, &collection_info)?; - - // use info.sender if None is passed - let minter: &str = match msg.minter.as_deref() { - Some(minter) => minter, - None => info.sender.as_str(), - }; - self.initialize_minter(deps.storage, deps.api, Some(minter))?; - - // use info.sender if None is passed - let creator: &str = match msg.creator.as_deref() { - Some(creator) => creator, - None => info.sender.as_str(), - }; - self.initialize_creator(deps.storage, deps.api, Some(creator))?; - - if let Some(withdraw_address) = msg.withdraw_address { - let creator = deps.api.addr_validate(creator)?; - self.set_withdraw_address(deps, &creator, withdraw_address)?; - } - - Ok(Response::default() - .add_attribute("minter", minter) - .add_attribute("creator", creator)) - } - - pub fn execute( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, - ) -> Result, ContractError> { - match msg { - ExecuteMsg::Mint { - token_id, - owner, - token_uri, - extension, - } => self.mint(deps, info, token_id, owner, token_uri, extension), - ExecuteMsg::Approve { - spender, - token_id, - expires, - } => self.approve(deps, env, info, spender, token_id, expires), - ExecuteMsg::Revoke { spender, token_id } => { - self.revoke(deps, env, info, spender, token_id) - } - ExecuteMsg::ApproveAll { operator, expires } => { - self.approve_all(deps, env, info, operator, expires) - } - ExecuteMsg::RevokeAll { operator } => self.revoke_all(deps, env, info, operator), - ExecuteMsg::TransferNft { - recipient, - token_id, - } => self.transfer_nft(deps, env, info, recipient, token_id), - ExecuteMsg::SendNft { - contract, - token_id, - msg, - } => self.send_nft(deps, env, info, contract, token_id, msg), - ExecuteMsg::Burn { token_id } => self.burn(deps, env, info, token_id), - #[allow(deprecated)] - ExecuteMsg::UpdateOwnership(action) => { - Self::update_minter_ownership(deps, env, info, action) - } - ExecuteMsg::UpdateMinterOwnership(action) => { - Self::update_minter_ownership(deps, env, info, action) - } - ExecuteMsg::UpdateCreatorOwnership(action) => { - Self::update_creator_ownership(deps, env, info, action) - } - ExecuteMsg::Extension { msg: _ } => Ok(Response::default()), - ExecuteMsg::SetWithdrawAddress { address } => { - self.set_withdraw_address(deps, &info.sender, address) - } - ExecuteMsg::RemoveWithdrawAddress {} => { - self.remove_withdraw_address(deps.storage, &info.sender) - } - ExecuteMsg::WithdrawFunds { amount } => self.withdraw_funds(deps.storage, &amount), - } - } -} - -// TODO pull this into some sort of trait extension?? impl< 'a, TMetadata, @@ -142,159 +14,13 @@ impl< TMetadataResponse, TCollectionInfoExtension, > - Cw721Contract< - 'a, + Cw721Execute< TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse, TCollectionInfoExtension, > -where - TMetadata: Serialize + DeserializeOwned + Clone, - TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, -{ - pub fn initialize_creator( - &self, - storage: &mut dyn Storage, - api: &dyn Api, - creator: Option<&str>, - ) -> StdResult> { - CREATOR.initialize_owner(storage, api, creator) - } - - pub fn initialize_minter( - &self, - storage: &mut dyn Storage, - api: &dyn Api, - minter: Option<&str>, - ) -> StdResult> { - MINTER.initialize_owner(storage, api, minter) - } - - pub fn mint( - &self, - deps: DepsMut, - info: MessageInfo, - token_id: String, - owner: String, - token_uri: Option, - extension: TMetadata, - ) -> Result, ContractError> { - MINTER.assert_owner(deps.storage, &info.sender)?; - - // create the token - let token = NftInfo { - owner: deps.api.addr_validate(&owner)?, - approvals: vec![], - token_uri, - extension, - }; - self.nft_info - .update(deps.storage, &token_id, |old| match old { - Some(_) => Err(ContractError::Claimed {}), - None => Ok(token), - })?; - - self.increment_tokens(deps.storage)?; - - Ok(Response::new() - .add_attribute("action", "mint") - .add_attribute("minter", info.sender) - .add_attribute("owner", owner) - .add_attribute("token_id", token_id)) - } - - pub fn update_minter_ownership( - deps: DepsMut, - env: Env, - info: MessageInfo, - action: Action, - ) -> Result, ContractError> { - let ownership = MINTER.update_ownership(deps, &env.block, &info.sender, action)?; - Ok(Response::new() - .add_attribute("update_minter_ownership", info.sender) - .add_attributes(ownership.into_attributes())) - } - - pub fn update_creator_ownership( - deps: DepsMut, - env: Env, - info: MessageInfo, - action: Action, - ) -> Result, ContractError> { - let ownership = CREATOR.update_ownership(deps, &env.block, &info.sender, action)?; - Ok(Response::new() - .add_attribute("update_creator_ownership", info.sender) - .add_attributes(ownership.into_attributes())) - } - - pub fn set_withdraw_address( - &self, - deps: DepsMut, - sender: &Addr, - address: String, - ) -> Result, ContractError> { - CREATOR.assert_owner(deps.storage, sender)?; - deps.api.addr_validate(&address)?; - self.withdraw_address.save(deps.storage, &address)?; - Ok(Response::new() - .add_attribute("action", "set_withdraw_address") - .add_attribute("address", address)) - } - - pub fn remove_withdraw_address( - &self, - storage: &mut dyn Storage, - sender: &Addr, - ) -> Result, ContractError> { - CREATOR.assert_owner(storage, sender)?; - let address = self.withdraw_address.may_load(storage)?; - match address { - Some(address) => { - self.withdraw_address.remove(storage); - Ok(Response::new() - .add_attribute("action", "remove_withdraw_address") - .add_attribute("address", address)) - } - None => Err(ContractError::NoWithdrawAddress {}), - } - } - - pub fn withdraw_funds( - &self, - storage: &mut dyn Storage, - amount: &Coin, - ) -> Result, ContractError> { - let withdraw_address = self.withdraw_address.may_load(storage)?; - match withdraw_address { - Some(address) => { - let msg = BankMsg::Send { - to_address: address, - amount: vec![amount.clone()], - }; - Ok(Response::new() - .add_message(msg) - .add_attribute("action", "withdraw_funds") - .add_attribute("amount", amount.amount.to_string()) - .add_attribute("denom", amount.denom.to_string())) - } - None => Err(ContractError::NoWithdrawAddress {}), - } - } -} - -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > Cw721Execute for Cw721Contract< 'a, TMetadata, @@ -310,293 +36,4 @@ where TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { - type Err = ContractError; - - fn transfer_nft( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - recipient: String, - token_id: String, - ) -> Result, ContractError> { - self._transfer_nft(deps, &env, &info, &recipient, &token_id)?; - - Ok(Response::new() - .add_attribute("action", "transfer_nft") - .add_attribute("sender", info.sender) - .add_attribute("recipient", recipient) - .add_attribute("token_id", token_id)) - } - - fn send_nft( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - contract: String, - token_id: String, - msg: Binary, - ) -> Result, ContractError> { - // Transfer token - self._transfer_nft(deps, &env, &info, &contract, &token_id)?; - - let send = Cw721ReceiveMsg { - sender: info.sender.to_string(), - token_id: token_id.clone(), - msg, - }; - - // Send message - Ok(Response::new() - .add_message(send.into_cosmos_msg(contract.clone())?) - .add_attribute("action", "send_nft") - .add_attribute("sender", info.sender) - .add_attribute("recipient", contract) - .add_attribute("token_id", token_id)) - } - - fn approve( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - spender: String, - token_id: String, - expires: Option, - ) -> Result, ContractError> { - self._update_approvals(deps, &env, &info, &spender, &token_id, true, expires)?; - - Ok(Response::new() - .add_attribute("action", "approve") - .add_attribute("sender", info.sender) - .add_attribute("spender", spender) - .add_attribute("token_id", token_id)) - } - - fn revoke( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - spender: String, - token_id: String, - ) -> Result, ContractError> { - self._update_approvals(deps, &env, &info, &spender, &token_id, false, None)?; - - Ok(Response::new() - .add_attribute("action", "revoke") - .add_attribute("sender", info.sender) - .add_attribute("spender", spender) - .add_attribute("token_id", token_id)) - } - - fn approve_all( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - operator: String, - expires: Option, - ) -> Result, ContractError> { - // reject expired data as invalid - let expires = expires.unwrap_or_default(); - if expires.is_expired(&env.block) { - return Err(ContractError::Expired {}); - } - - // set the operator for us - let operator_addr = deps.api.addr_validate(&operator)?; - self.operators - .save(deps.storage, (&info.sender, &operator_addr), &expires)?; - - Ok(Response::new() - .add_attribute("action", "approve_all") - .add_attribute("sender", info.sender) - .add_attribute("operator", operator)) - } - - fn revoke_all( - &self, - deps: DepsMut, - _env: Env, - info: MessageInfo, - operator: String, - ) -> Result, ContractError> { - let operator_addr = deps.api.addr_validate(&operator)?; - self.operators - .remove(deps.storage, (&info.sender, &operator_addr)); - - Ok(Response::new() - .add_attribute("action", "revoke_all") - .add_attribute("sender", info.sender) - .add_attribute("operator", operator)) - } - - fn burn( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - token_id: String, - ) -> Result, ContractError> { - let token = self.nft_info.load(deps.storage, &token_id)?; - self.check_can_send(deps.as_ref(), &env, &info, &token)?; - - self.nft_info.remove(deps.storage, &token_id)?; - self.decrement_tokens(deps.storage)?; - - Ok(Response::new() - .add_attribute("action", "burn") - .add_attribute("sender", info.sender) - .add_attribute("token_id", token_id)) - } -} - -// helpers -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > - Cw721Contract< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > -where - TMetadata: Serialize + DeserializeOwned + Clone, - TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, -{ - pub fn _transfer_nft( - &self, - deps: DepsMut, - env: &Env, - info: &MessageInfo, - recipient: &str, - token_id: &str, - ) -> Result, ContractError> { - let mut token = self.nft_info.load(deps.storage, token_id)?; - // ensure we have permissions - self.check_can_send(deps.as_ref(), env, info, &token)?; - // set owner and remove existing approvals - token.owner = deps.api.addr_validate(recipient)?; - token.approvals = vec![]; - self.nft_info.save(deps.storage, token_id, &token)?; - Ok(token) - } - - #[allow(clippy::too_many_arguments)] - pub fn _update_approvals( - &self, - deps: DepsMut, - env: &Env, - info: &MessageInfo, - spender: &str, - token_id: &str, - // if add == false, remove. if add == true, remove then set with this expiration - add: bool, - expires: Option, - ) -> Result, ContractError> { - let mut token = self.nft_info.load(deps.storage, token_id)?; - // ensure we have permissions - self.check_can_approve(deps.as_ref(), env, info, &token)?; - - // update the approval list (remove any for the same spender before adding) - let spender_addr = deps.api.addr_validate(spender)?; - token.approvals.retain(|apr| apr.spender != spender_addr); - - // only difference between approve and revoke - if add { - // reject expired data as invalid - let expires = expires.unwrap_or_default(); - if expires.is_expired(&env.block) { - return Err(ContractError::Expired {}); - } - let approval = Approval { - spender: spender_addr, - expires, - }; - token.approvals.push(approval); - } - - self.nft_info.save(deps.storage, token_id, &token)?; - - Ok(token) - } - - /// returns true iff the sender can execute approve or reject on the contract - pub fn check_can_approve( - &self, - deps: Deps, - env: &Env, - info: &MessageInfo, - token: &NftInfo, - ) -> Result<(), ContractError> { - // owner can approve - if token.owner == info.sender { - return Ok(()); - } - // operator can approve - let op = self - .operators - .may_load(deps.storage, (&token.owner, &info.sender))?; - match op { - Some(ex) => { - if ex.is_expired(&env.block) { - Err(ContractError::Ownership(OwnershipError::NotOwner)) - } else { - Ok(()) - } - } - None => Err(ContractError::Ownership(OwnershipError::NotOwner)), - } - } - - /// returns true iff the sender can transfer ownership of the token - pub fn check_can_send( - &self, - deps: Deps, - env: &Env, - info: &MessageInfo, - token: &NftInfo, - ) -> Result<(), ContractError> { - // owner can send - if token.owner == info.sender { - return Ok(()); - } - - // any non-expired token approval can send - if token - .approvals - .iter() - .any(|apr| apr.spender == info.sender && !apr.is_expired(&env.block)) - { - return Ok(()); - } - - // operator can send - let op = self - .operators - .may_load(deps.storage, (&token.owner, &info.sender))?; - match op { - Some(ex) => { - if ex.is_expired(&env.block) { - Err(ContractError::Ownership(OwnershipError::NotOwner)) - } else { - Ok(()) - } - } - None => Err(ContractError::Ownership(OwnershipError::NotOwner)), - } - } } diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index 61d0c5a33..3dc4162e8 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -1,17 +1,9 @@ pub mod error; -mod execute; -pub mod helpers; +pub mod execute; pub mod msg; -mod query; +pub mod query; pub mod state; -#[cfg(test)] -mod contract_tests; -#[cfg(test)] -mod multi_tests; - -pub use crate::error::ContractError; -pub use crate::msg::{ExecuteMsg, InstantiateMsg, MinterResponse, QueryMsg}; pub use crate::state::Cw721Contract; // These types are re-exported so that contracts interacting with this @@ -24,30 +16,24 @@ pub use cw_ownable::{Action, Ownership, OwnershipError}; use cosmwasm_std::Empty; -// These are simple types to let us handle empty extensions -pub use cw721::EmptyCollectionInfoExtension; -pub use cw721::EmptyExtension; - // Version info for migration pub const CONTRACT_NAME: &str = "crates.io:cw721-base"; pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub mod entry { - use self::{ - msg::MigrateMsg, - state::{token_owner_idx, NftInfo, TokenIndexes, CREATOR, MINTER}, - }; use super::*; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; - use cosmwasm_std::{ - Addr, Api, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult, Storage, + use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; + use cw721::{ + error::Cw721ContractError, + execute::Cw721Execute, + msg::{Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, Cw721QueryMsg}, + query::Cw721Query, + state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, }; - use cw721::CollectionInfo; - use cw_ownable::none_or; - use cw_storage_plus::{IndexedMap, Item, MultiIndex}; // This makes a conscious choice on the various generics used by the contract #[cfg_attr(not(feature = "library"), entry_point)] @@ -55,18 +41,16 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, - ) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - let tract = Cw721Contract::< - EmptyExtension, + msg: Cw721InstantiateMsg, + ) -> Result { + let contract = Cw721Contract::< + DefaultOptionMetadataExtension, Empty, Empty, Empty, - EmptyCollectionInfoExtension, + DefaultOptionCollectionInfoExtension, >::default(); - tract.instantiate(deps, env, info, msg) + contract.instantiate(deps, env, info, msg, CONTRACT_NAME, CONTRACT_VERSION) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -74,442 +58,51 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: ExecuteMsg, - ) -> Result { - let tract = Cw721Contract::::default(); - tract.execute(deps, env, info, msg) - } - - #[cfg_attr(not(feature = "library"), entry_point)] - pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { - let tract = Cw721Contract::::default(); - tract.query(deps, env, msg) + msg: Cw721ExecuteMsg< + DefaultOptionMetadataExtension, + Empty, + DefaultOptionCollectionInfoExtension, + >, + ) -> Result { + let contract = Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + contract.execute(deps, env, info, msg) } #[cfg_attr(not(feature = "library"), entry_point)] - pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> Result { - let response = Response::::default(); - // first migrate legacy data ... - let response = - migrate_legacy_minter_and_creator(deps.storage, deps.api, &env, &msg, response)?; - let response = migrate_legacy_collection_info(deps.storage, &env, &msg, response)?; - let response = migrate_legacy_tokens(deps.storage, &env, &msg, response)?; - // ... then migrate - let response = migrate_version(deps.storage, &env, &msg, response)?; - // ... and update creator and minter AFTER legacy migration - let response = migrate_creator(deps.storage, deps.api, &env, &msg, response)?; - let response = migrate_minter(deps.storage, deps.api, &env, &msg, response)?; - Ok(response) - } - - pub fn migrate_version( - storage: &mut dyn Storage, - _env: &Env, - _msg: &MigrateMsg, - response: Response, - ) -> StdResult { - let response = response - .add_attribute("from_version", cw2::get_contract_version(storage)?.version) - .add_attribute("to_version", CONTRACT_VERSION); - - // update contract version - cw2::set_contract_version(storage, CONTRACT_NAME, CONTRACT_VERSION)?; - Ok(response) - } - - pub fn migrate_creator( - storage: &mut dyn Storage, - api: &dyn Api, - _env: &Env, - msg: &MigrateMsg, - response: Response, - ) -> StdResult { - match msg { - MigrateMsg::WithUpdate { creator, .. } => { - if let Some(creator) = creator { - CREATOR.initialize_owner(storage, api, Some(creator.as_str()))?; - return Ok(response.add_attribute("creator", creator)); - } - } - } - Ok(response) - } - - pub fn migrate_minter( - storage: &mut dyn Storage, - api: &dyn Api, - _env: &Env, - msg: &MigrateMsg, - response: Response, - ) -> StdResult { - match msg { - MigrateMsg::WithUpdate { minter, .. } => { - if let Some(minter) = minter { - MINTER.initialize_owner(storage, api, Some(minter.as_str()))?; - return Ok(response.add_attribute("creator", minter)); - } - } - } - Ok(response) - } - - /// Migrates only in case ownership is not present - /// !!! Important note here: !!! - /// - creator owns the contract and can update collection info - /// - minter can mint new tokens - /// - /// Before v0.19.0 there were confusing naming conventions: - /// - v0.17.0: minter was replaced by cw_ownable, as a result minter is owner - /// - v0.16.0 and below: minter was stored in dedicated `minter` store (so NOT using cw_ownable at all) - pub fn migrate_legacy_minter_and_creator( - storage: &mut dyn Storage, - api: &dyn Api, - _env: &Env, - _msg: &MigrateMsg, - response: Response, - ) -> Result { - let minter = MINTER.item.may_load(storage)?; - // no migration in case minter is already set - if minter.is_some() { - return Ok(response); - } - // in v0.17/18 cw_ownable::OWNERSHIP was used for minter, now it is used for creator - let ownership_previously_used_as_minter = CREATOR.item.may_load(storage)?; - let creator_and_minter = match ownership_previously_used_as_minter { - // v0.18 migration - Some(ownership) => { - // owner is used for both: creator and minter - // since it is already set for creator, we only need to migrate minter - let owner = ownership.owner.map(|a| a.to_string()); - MINTER.initialize_owner(storage, api, owner.as_deref())?; - owner - } - // v0.17 and older migration - None => { - let legacy_minter_store: Item = Item::new("minter"); - let legacy_minter = legacy_minter_store.load(storage)?; - MINTER.initialize_owner(storage, api, Some(legacy_minter.as_str()))?; - CREATOR.initialize_owner(storage, api, Some(legacy_minter.as_str()))?; - Some(legacy_minter.to_string()) - } - }; - Ok(response.add_attribute("creator_and_minter", none_or(creator_and_minter.as_ref()))) - } - - /// Migrates only in case collection_info is not present - pub fn migrate_legacy_collection_info( - storage: &mut dyn Storage, - env: &Env, - _msg: &MigrateMsg, - response: Response, - ) -> Result { + pub fn query( + deps: Deps, + env: Env, + msg: Cw721QueryMsg, + ) -> StdResult { let contract = Cw721Contract::< - EmptyExtension, + DefaultOptionMetadataExtension, Empty, Empty, Empty, - EmptyCollectionInfoExtension, + DefaultOptionCollectionInfoExtension, >::default(); - match contract.collection_info.may_load(storage)? { - Some(_) => Ok(response), - None => { - // contract info is legacy collection info - let legacy_collection_info_store: Item = - Item::new("nft_info"); - let legacy_collection_info = legacy_collection_info_store.load(storage)?; - let collection_info = CollectionInfo { - name: legacy_collection_info.name.clone(), - symbol: legacy_collection_info.symbol.clone(), - extension: None, - updated_at: env.block.time, - }; - contract.collection_info.save(storage, &collection_info)?; - Ok(response - .add_attribute("migrated collection name", legacy_collection_info.name) - .add_attribute("migrated collection symbol", legacy_collection_info.symbol)) - } - } + contract.query(deps, env, msg) } - /// Migrates only in case collection_info is not present - pub fn migrate_legacy_tokens( - storage: &mut dyn Storage, - _env: &Env, - _msg: &MigrateMsg, - response: Response, - ) -> StdResult { - let contract = Cw721Contract::::default(); - match contract.nft_info.is_empty(storage) { - false => Ok(response), - true => { - let indexes = TokenIndexes { - owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"), - }; - let legacy_tokens_store: IndexedMap< - &str, - NftInfo, - TokenIndexes, - > = IndexedMap::new("tokens", indexes); - let keys = legacy_tokens_store - .keys(storage, None, None, Order::Ascending) - .collect::>>()?; - for key in keys { - let legacy_token = legacy_tokens_store.load(storage, &key)?; - contract.nft_info.save(storage, &key, &legacy_token)?; - } - Ok(response) - } - } - } -} - -#[cfg(test)] -mod tests { - use cosmwasm_std::{ - testing::{mock_dependencies, mock_env, mock_info}, - Addr, Order, StdResult, - }; - use cw2::ContractVersion; - use cw721::{CollectionInfo, Cw721Query}; - use cw_storage_plus::{IndexedMap, Item, MultiIndex}; - - use crate::{ - msg::MigrateMsg, - query::MAX_LIMIT, - state::{token_owner_idx, NftInfo, TokenIndexes, CREATOR, MINTER}, - }; - - use super::*; - - /// Make sure cw2 version info is properly initialized during instantiation. - #[test] - fn proper_cw2_initialization() { - let mut deps = mock_dependencies(); - - entry::instantiate( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - InstantiateMsg { - name: "collection_name".into(), - symbol: "collection_symbol".into(), - collection_info_extension: None, - minter: Some("minter".into()), - creator: Some("creator".into()), - withdraw_address: None, - }, - ) - .unwrap(); - - let minter = MINTER - .get_ownership(deps.as_ref().storage) - .unwrap() - .owner - .map(|a| a.into_string()); - assert_eq!(minter, Some("minter".to_string())); - - let creator = CREATOR - .get_ownership(deps.as_ref().storage) - .unwrap() - .owner - .map(|a| a.into_string()); - assert_eq!(creator, Some("creator".to_string())); - - let version = cw2::get_contract_version(deps.as_ref().storage).unwrap(); - assert_eq!( - version, - ContractVersion { - contract: CONTRACT_NAME.into(), - version: CONTRACT_VERSION.into(), - }, - ); - } - - #[test] - fn proper_owner_initialization() { - let mut deps = mock_dependencies(); - - let info_owner = mock_info("owner", &[]); - entry::instantiate( - deps.as_mut(), - mock_env(), - info_owner.clone(), - InstantiateMsg { - name: "collection_name".into(), - symbol: "collection_symbol".into(), - collection_info_extension: None, - creator: None, - minter: None, - withdraw_address: None, - }, - ) - .unwrap(); - - let minter = MINTER.item.load(deps.as_ref().storage).unwrap().owner; - assert_eq!(minter, Some(info_owner.sender)); - let creator = CREATOR.item.load(deps.as_ref().storage).unwrap().owner; - assert_eq!(creator, Some(Addr::unchecked("owner"))); - } - - #[test] - fn test_migrate() { - let mut deps = mock_dependencies(); - - let env = mock_env(); - use cw721_base_016 as v16; - v16::entry::instantiate( - deps.as_mut(), - env.clone(), - mock_info("owner", &[]), - v16::InstantiateMsg { - name: "legacy_name".into(), - symbol: "legacy_symbol".into(), - minter: "legacy_minter".into(), - }, - ) - .unwrap(); - - // mint 200 NFTs before migration - for i in 0..200 { - let info = mock_info("legacy_minter", &[]); - let msg = v16::ExecuteMsg::Mint(v16::msg::MintMsg { - token_id: i.to_string(), - owner: "owner".into(), - token_uri: None, - extension: None, - }); - v16::entry::execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - } - - // assert new data before migration: - // - ownership and collection info throws NotFound Error - MINTER.item.load(deps.as_ref().storage).unwrap_err(); // cw_ownable in v16 is used for minter + #[cfg_attr(not(feature = "library"), entry_point)] + pub fn migrate( + deps: DepsMut, + env: Env, + msg: Cw721MigrateMsg, + ) -> Result { let contract = Cw721Contract::< - EmptyExtension, + DefaultOptionMetadataExtension, Empty, Empty, Empty, - EmptyCollectionInfoExtension, + DefaultOptionCollectionInfoExtension, >::default(); - contract.collection_info(deps.as_ref()).unwrap_err(); - // query on minter and creator store also throws NotFound Error - MINTER.get_ownership(deps.as_ref().storage).unwrap_err(); - CREATOR.get_ownership(deps.as_ref().storage).unwrap_err(); - // - no tokens - let all_tokens = contract - .all_tokens(deps.as_ref(), None, Some(MAX_LIMIT)) - .unwrap(); - assert_eq!(all_tokens.tokens.len(), 0); - - // assert legacy data before migration: - // - version - let version = cw2::get_contract_version(deps.as_ref().storage) - .unwrap() - .version; - assert_eq!(version, "0.16.0"); - // - legacy minter is set - let legacy_minter_store: Item = Item::new("minter"); - let legacy_minter = legacy_minter_store.load(deps.as_ref().storage).unwrap(); - assert_eq!(legacy_minter, "legacy_minter"); - // - legacy collection info is set - let legacy_collection_info_store: Item = - Item::new("nft_info"); - let legacy_collection_info = legacy_collection_info_store - .load(deps.as_ref().storage) - .unwrap(); - assert_eq!(legacy_collection_info.name, "legacy_name"); - assert_eq!(legacy_collection_info.symbol, "legacy_symbol"); - // - legacy tokens are set - let indexes = TokenIndexes { - owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"), - }; - let legacy_tokens_store: IndexedMap< - &str, - NftInfo, - TokenIndexes, - > = IndexedMap::new("tokens", indexes); - let keys = legacy_tokens_store - .keys(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() - .unwrap(); - assert_eq!(keys.len(), 200); - for key in keys { - let legacy_token = legacy_tokens_store - .load(deps.as_ref().storage, &key) - .unwrap(); - assert_eq!(legacy_token.owner.as_str(), "owner"); - } - - entry::migrate( - deps.as_mut(), - env.clone(), - MigrateMsg::WithUpdate { - minter: None, - creator: None, - }, - ) - .unwrap(); - - // version - let version = cw2::get_contract_version(deps.as_ref().storage) - .unwrap() - .version; - assert_eq!(version, CONTRACT_VERSION); - assert_ne!(version, "0.16.0"); - - // assert minter ownership - let minter_ownership = MINTER - .get_ownership(deps.as_ref().storage) - .unwrap() - .owner - .map(|a| a.into_string()); - assert_eq!(minter_ownership, Some("legacy_minter".to_string())); - - // assert creator ownership - let creator_ownership = CREATOR - .get_ownership(deps.as_ref().storage) - .unwrap() - .owner - .map(|a| a.into_string()); - assert_eq!(creator_ownership, Some("legacy_minter".to_string())); - - // assert collection info - let collection_info = contract.collection_info(deps.as_ref()).unwrap(); - let legacy_contract_info = CollectionInfo { - name: "legacy_name".to_string(), - symbol: "legacy_symbol".to_string(), - extension: None, - updated_at: env.block.time, - }; - assert_eq!(collection_info, legacy_contract_info); - - // assert tokens - let all_tokens = contract - .all_tokens(deps.as_ref(), None, Some(MAX_LIMIT)) - .unwrap(); - assert_eq!(all_tokens.tokens.len(), 200); - - // assert legacy data is still there (allowing backward migration in case of issues) - // - minter - let legacy_minter = legacy_minter_store.load(deps.as_ref().storage).unwrap(); - assert_eq!(legacy_minter, "legacy_minter"); - // - collection info - let legacy_collection_info = legacy_collection_info_store - .load(deps.as_ref().storage) - .unwrap(); - assert_eq!(legacy_collection_info.name, "legacy_name"); - assert_eq!(legacy_collection_info.symbol, "legacy_symbol"); - // - tokens - let keys = legacy_tokens_store - .keys(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() - .unwrap(); - assert_eq!(keys.len(), 200); - for key in keys { - let legacy_token = legacy_tokens_store - .load(deps.as_ref().storage, &key) - .unwrap(); - assert_eq!(legacy_token.owner.as_str(), "owner"); - } + contract.migrate(deps, env, msg, CONTRACT_NAME, CONTRACT_VERSION) } } diff --git a/contracts/cw721-base/src/msg.rs b/contracts/cw721-base/src/msg.rs index f06d616be..7cfb7a5c1 100644 --- a/contracts/cw721-base/src/msg.rs +++ b/contracts/cw721-base/src/msg.rs @@ -1,228 +1,5 @@ -use std::{os::raw, result}; - -use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Binary, Coin, Empty}; -use cw721::Expiration; -use cw_ownable::{Action, Ownership}; -use schemars::JsonSchema; - -#[cw_serde] -pub struct InstantiateMsg { - /// Name of the NFT contract - pub name: String, - /// Symbol of the NFT contract - pub symbol: String, - - pub collection_info_extension: TCollectionInfoExtension, - - /// The minter is the only one who can create new NFTs. - /// This is designed for a base NFT that is controlled by an external program - /// or contract. You will likely replace this with custom logic in custom NFTs - pub minter: Option, - - /// The creator is the only who can update collection info. - pub creator: Option, - - pub withdraw_address: Option, -} - -/// This is like Cw721ExecuteMsg but we add a Mint command for an owner -/// to make this stand-alone. You will likely want to remove mint and -/// use other control logic in any contract that inherits this. -#[cw_serde] -pub enum ExecuteMsg { - #[deprecated(since = "0.19.0", note = "Please use UpdateMinterOwnership instead")] - UpdateOwnership(Action), - UpdateMinterOwnership(Action), - UpdateCreatorOwnership(Action), - /// Transfer is a base message to move a token to another account without triggering actions - TransferNft { - recipient: String, - token_id: String, - }, - /// Send is a base message to transfer a token to a contract and trigger an action - /// on the receiving contract. - SendNft { - contract: String, - token_id: String, - msg: Binary, - }, - /// Allows operator to transfer / send the token from the owner's account. - /// If expiration is set, then this allowance has a time/height limit - Approve { - spender: String, - token_id: String, - expires: Option, - }, - /// Remove previously granted Approval - Revoke { - spender: String, - token_id: String, - }, - /// Allows operator to transfer / send any token from the owner's account. - /// If expiration is set, then this allowance has a time/height limit - ApproveAll { - operator: String, - expires: Option, - }, - /// Remove previously granted ApproveAll permission - RevokeAll { - operator: String, - }, - - /// Mint a new NFT, can only be called by the contract minter - Mint { - /// Unique ID of the NFT - token_id: String, - /// The owner of the newly minter NFT - owner: String, - /// Universal resource identifier for this NFT - /// Should point to a JSON file that conforms to the ERC721 - /// Metadata JSON Schema - token_uri: Option, - /// Any custom extension used by this contract - extension: TMetadata, - }, - - /// Burn an NFT the sender has access to - Burn { - token_id: String, - }, - - /// Extension msg - Extension { - msg: TExtensionExecuteMsg, - }, - - /// Sets address to send withdrawn fees to. Only owner can call this. - SetWithdrawAddress { - address: String, - }, - /// Removes the withdraw address, so fees are sent to the contract. Only owner can call this. - RemoveWithdrawAddress {}, - /// Withdraw from the contract to the given address. Anyone can call this, - /// which is okay since withdraw address has been set by owner. - WithdrawFunds { - amount: Coin, - }, -} - -#[cw_serde] -#[derive(QueryResponses)] -pub enum QueryMsg { - /// Return the owner of the given token, error if token does not exist - #[returns(cw721::OwnerOfResponse)] - OwnerOf { - token_id: String, - /// unset or false will filter out expired approvals, you must set to true to see them - include_expired: Option, - }, - /// Return operator that can access all of the owner's tokens. - #[returns(cw721::ApprovalResponse)] - Approval { - token_id: String, - spender: String, - include_expired: Option, - }, - /// Return approvals that a token has - #[returns(cw721::ApprovalsResponse)] - Approvals { - token_id: String, - include_expired: Option, - }, - /// Return approval of a given operator for all tokens of an owner, error if not set - #[returns(cw721::OperatorResponse)] - Operator { - owner: String, - operator: String, - include_expired: Option, - }, - /// List all operators that can access all of the owner's tokens - #[returns(cw721::OperatorsResponse)] - AllOperators { - owner: String, - /// unset or false will filter out expired items, you must set to true to see them - include_expired: Option, - start_after: Option, - limit: Option, - }, - /// Total number of tokens issued - #[returns(cw721::NumTokensResponse)] - NumTokens {}, - - #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] - #[returns(cw721::CollectionInfo)] - ContractInfo {}, - - /// With MetaData Extension. - /// Returns top-level metadata about the contract - #[returns(cw721::CollectionInfo)] - GetCollectionInfo {}, - - #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] - #[returns(Ownership)] - Ownership {}, - - #[returns(Ownership)] - GetMinterOwnership {}, - - #[returns(Ownership)] - GetCreatorOwnership {}, - - /// With MetaData Extension. - /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* - /// but directly from the contract - #[returns(cw721::NftInfoResponse)] - NftInfo { token_id: String }, - /// With MetaData Extension. - /// Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization - /// for clients - #[returns(cw721::AllNftInfoResponse)] - AllNftInfo { - token_id: String, - /// unset or false will filter out expired approvals, you must set to true to see them - include_expired: Option, - }, - - /// With Enumerable extension. - /// Returns all tokens owned by the given address, [] if unset. - #[returns(cw721::TokensResponse)] - Tokens { - owner: String, - start_after: Option, - limit: Option, - }, - /// With Enumerable extension. - /// Requires pagination. Lists all token_ids controlled by the contract. - #[returns(cw721::TokensResponse)] - AllTokens { - start_after: Option, - limit: Option, - }, - - /// Return the minter - #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] - #[returns(MinterResponse)] - Minter {}, - - /// Extension query - #[returns(())] - Extension { msg: TMetadataResponse }, - - #[returns(Option)] - GetWithdrawAddress {}, -} - -/// Shows who can mint these tokens -#[cw_serde] -pub struct MinterResponse { - pub minter: Option, -} - -#[cw_serde] -pub enum MigrateMsg { - WithUpdate { - minter: Option, - creator: Option, - }, -} +// expose to all others using contract, so others dont need to import cw721 +pub use cw721::msg::{ + Cw721ExecuteMsg as ExecuteMsg, Cw721InstantiateMsg as InstantiateMsg, + Cw721MigrateMsg as MigrateMsg, Cw721QueryMsg as QueryMsg, *, +}; diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index 0ef508d79..98dbc236e 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -1,25 +1,10 @@ -use cw_ownable::Ownership; +use cosmwasm_std::CustomMsg; +// expose to all others using contract, so others dont need to import cw721 +pub use cw721::query::*; use serde::de::DeserializeOwned; use serde::Serialize; -use cosmwasm_std::{ - to_json_binary, Addr, Binary, BlockInfo, CustomMsg, Deps, Env, Order, StdError, StdResult, - Storage, -}; - -use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721Query, - Expiration, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, - OwnerOfResponse, TokensResponse, -}; -use cw_storage_plus::Bound; -use cw_utils::maybe_addr; - -use crate::msg::{MinterResponse, QueryMsg}; -use crate::state::{Approval, Cw721Contract, NftInfo, CREATOR, MINTER}; - -pub const DEFAULT_LIMIT: u32 = 10; -pub const MAX_LIMIT: u32 = 1000; +use crate::Cw721Contract; impl< 'a, @@ -28,7 +13,7 @@ impl< TExtensionExecuteMsg, TMetadataResponse, TCollectionInfoExtension, - > Cw721Query + > Cw721Query for Cw721Contract< 'a, TMetadata, @@ -44,384 +29,4 @@ where TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { - fn collection_info(&self, deps: Deps) -> StdResult> { - self.collection_info.load(deps.storage) - } - - fn num_tokens(&self, deps: Deps) -> StdResult { - let count = self.token_count(deps.storage)?; - Ok(NumTokensResponse { count }) - } - - fn nft_info(&self, deps: Deps, token_id: String) -> StdResult> { - let info = self.nft_info.load(deps.storage, &token_id)?; - Ok(NftInfoResponse { - token_uri: info.token_uri, - extension: info.extension, - }) - } - - fn owner_of( - &self, - deps: Deps, - env: Env, - token_id: String, - include_expired: bool, - ) -> StdResult { - let info = self.nft_info.load(deps.storage, &token_id)?; - Ok(OwnerOfResponse { - owner: info.owner.to_string(), - approvals: humanize_approvals(&env.block, &info, include_expired), - }) - } - - /// operator returns the approval status of an operator for a given owner if exists - fn operator( - &self, - deps: Deps, - env: Env, - owner: String, - operator: String, - include_expired: bool, - ) -> StdResult { - let owner_addr = deps.api.addr_validate(&owner)?; - let operator_addr = deps.api.addr_validate(&operator)?; - - let info = self - .operators - .may_load(deps.storage, (&owner_addr, &operator_addr))?; - - if let Some(expires) = info { - if !include_expired && expires.is_expired(&env.block) { - return Err(StdError::not_found("Approval not found")); - } - - return Ok(OperatorResponse { - approval: cw721::Approval { - spender: operator, - expires, - }, - }); - } - - Err(StdError::not_found("Approval not found")) - } - - /// operators returns all operators owner given access to - fn operators( - &self, - deps: Deps, - env: Env, - owner: String, - include_expired: bool, - start_after: Option, - limit: Option, - ) -> StdResult { - 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.as_ref().map(Bound::exclusive); - - let owner_addr = deps.api.addr_validate(&owner)?; - let res: StdResult> = 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 { - let token = self.nft_info.load(deps.storage, &token_id)?; - - // token owner has absolute approval - if token.owner == spender { - let approval = cw721::Approval { - spender: token.owner.to_string(), - 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| cw721::Approval { - spender: a.spender.into_string(), - expires: a.expires, - }) - .collect(); - - if filtered.is_empty() { - return Err(StdError::not_found("Approval not found")); - } - // we expect only one item - let approval = filtered[0].clone(); - - Ok(ApprovalResponse { approval }) - } - - /// approvals returns all approvals owner given access to - fn approvals( - &self, - deps: Deps, - env: Env, - token_id: String, - include_expired: bool, - ) -> StdResult { - let token = self.nft_info.load(deps.storage, &token_id)?; - let approvals: Vec<_> = token - .approvals - .into_iter() - .filter(|t| include_expired || !t.is_expired(&env.block)) - .map(|a| cw721::Approval { - spender: a.spender.into_string(), - expires: a.expires, - }) - .collect(); - - Ok(ApprovalsResponse { approvals }) - } - - fn tokens( - &self, - deps: Deps, - owner: String, - start_after: Option, - limit: Option, - ) -> StdResult { - let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; - let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); - - let owner_addr = deps.api.addr_validate(&owner)?; - let tokens: Vec = self - .nft_info - .idx - .owner - .prefix(owner_addr) - .keys(deps.storage, start, None, Order::Ascending) - .take(limit) - .collect::>>()?; - - Ok(TokensResponse { tokens }) - } - - fn all_tokens( - &self, - deps: Deps, - start_after: Option, - limit: Option, - ) -> StdResult { - let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; - let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); - - let tokens: StdResult> = self - .nft_info - .range(deps.storage, start, None, Order::Ascending) - .take(limit) - .map(|item| item.map(|(k, _)| k)) - .collect(); - - Ok(TokensResponse { tokens: tokens? }) - } - - fn all_nft_info( - &self, - deps: Deps, - env: Env, - token_id: String, - include_expired: bool, - ) -> StdResult> { - let info = self.nft_info.load(deps.storage, &token_id)?; - Ok(AllNftInfoResponse { - access: OwnerOfResponse { - owner: info.owner.to_string(), - approvals: humanize_approvals(&env.block, &info, include_expired), - }, - info: NftInfoResponse { - token_uri: info.token_uri, - extension: info.extension, - }, - }) - } -} - -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > - Cw721Contract< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > -where - TMetadata: Serialize + DeserializeOwned + Clone, - TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, -{ - pub fn query( - &self, - deps: Deps, - env: Env, - msg: QueryMsg, - ) -> StdResult { - match msg { - #[allow(deprecated)] - QueryMsg::Minter {} => to_json_binary(&self.minter(deps)?), - #[allow(deprecated)] - QueryMsg::ContractInfo {} => to_json_binary(&self.collection_info(deps)?), - QueryMsg::GetCollectionInfo {} => to_json_binary(&self.collection_info(deps)?), - QueryMsg::NftInfo { token_id } => to_json_binary(&self.nft_info(deps, token_id)?), - QueryMsg::OwnerOf { - token_id, - include_expired, - } => to_json_binary(&self.owner_of( - deps, - env, - token_id, - include_expired.unwrap_or(false), - )?), - QueryMsg::AllNftInfo { - token_id, - include_expired, - } => to_json_binary(&self.all_nft_info( - deps, - env, - token_id, - include_expired.unwrap_or(false), - )?), - QueryMsg::Operator { - owner, - operator, - include_expired, - } => to_json_binary(&self.operator( - deps, - env, - owner, - operator, - include_expired.unwrap_or(false), - )?), - QueryMsg::AllOperators { - owner, - include_expired, - start_after, - limit, - } => to_json_binary(&self.operators( - deps, - env, - owner, - include_expired.unwrap_or(false), - start_after, - limit, - )?), - QueryMsg::NumTokens {} => to_json_binary(&self.num_tokens(deps)?), - QueryMsg::Tokens { - owner, - start_after, - limit, - } => to_json_binary(&self.tokens(deps, owner, start_after, limit)?), - QueryMsg::AllTokens { start_after, limit } => { - to_json_binary(&self.all_tokens(deps, start_after, limit)?) - } - QueryMsg::Approval { - token_id, - spender, - include_expired, - } => to_json_binary(&self.approval( - deps, - env, - token_id, - spender, - include_expired.unwrap_or(false), - )?), - QueryMsg::Approvals { - token_id, - include_expired, - } => to_json_binary(&self.approvals( - deps, - env, - token_id, - include_expired.unwrap_or(false), - )?), - #[allow(deprecated)] - QueryMsg::Ownership {} => to_json_binary(&self.minter_ownership(deps.storage)?), - QueryMsg::GetMinterOwnership {} => { - to_json_binary(&self.minter_ownership(deps.storage)?) - } - QueryMsg::GetCreatorOwnership {} => { - to_json_binary(&self.creator_ownership(deps.storage)?) - } - QueryMsg::Extension { msg: _ } => Ok(Binary::default()), - QueryMsg::GetWithdrawAddress {} => { - to_json_binary(&self.withdraw_address.may_load(deps.storage)?) - } - } - } - - #[deprecated(since = "0.19.0", note = "Please use minter_ownership instead")] - pub fn minter(&self, deps: Deps) -> StdResult { - let minter = MINTER - .get_ownership(deps.storage)? - .owner - .map(|a| a.into_string()); - - Ok(MinterResponse { minter }) - } - - pub fn minter_ownership(&self, storage: &dyn Storage) -> StdResult> { - MINTER.get_ownership(storage) - } - - pub fn creator_ownership(&self, storage: &dyn Storage) -> StdResult> { - CREATOR.get_ownership(storage) - } -} - -fn parse_approval(item: StdResult<(Addr, Expiration)>) -> StdResult { - item.map(|(spender, expires)| cw721::Approval { - spender: spender.to_string(), - expires, - }) -} - -fn humanize_approvals( - block: &BlockInfo, - info: &NftInfo, - include_expired: bool, -) -> Vec { - info.approvals - .iter() - .filter(|apr| include_expired || !apr.is_expired(block)) - .map(humanize_approval) - .collect() -} - -fn humanize_approval(approval: &Approval) -> cw721::Approval { - cw721::Approval { - spender: approval.spender.to_string(), - expires: approval.expires, - } } diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 4dc257d38..a05bf8987 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -1,21 +1,8 @@ -use cw_ownable::{OwnershipStore, OWNERSHIP_KEY}; -use schemars::JsonSchema; +use cosmwasm_std::CustomMsg; +// expose to all others using contract, so others dont need to import cw721 +pub use cw721::state::*; use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; -use std::marker::PhantomData; - -use cosmwasm_std::{Addr, BlockInfo, CustomMsg, StdResult, Storage}; - -use cw721::{CollectionInfo, Cw721, Expiration}; -use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; - -/// Creator owns this contract and can update collection info! -/// !!! Important note here: !!! -/// - creator is stored using using cw-ownable's OWNERSHIP singleton, so it is not stored here -/// - in release v0.18.0 it was used for minter (which is confusing), but now it is used for creator -pub const CREATOR: OwnershipStore = OwnershipStore::new(OWNERSHIP_KEY); -/// - minter is stored in the contract storage using cw_ownable::OwnershipStore (same as for OWNERSHIP but with different key) -pub const MINTER: OwnershipStore = OwnershipStore::new("collection_minter"); +use serde::Serialize; pub struct Cw721Contract< 'a, @@ -30,44 +17,14 @@ pub struct Cw721Contract< TExtensionExecuteMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { - /// Note: do not use deprecated/legacy key "nft_info"! - pub collection_info: Item<'a, CollectionInfo>, - pub token_count: Item<'a, u64>, - /// Stored as (granter, operator) giving operator full control over granter's account - pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, - /// Note: do not use deprecated/legacy keys "tokens" and "tokens__owner"! - pub nft_info: IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, TMetadata>>, - pub withdraw_address: Item<'a, String>, - - pub(crate) _custom_response: PhantomData, - pub(crate) _custom_query: PhantomData, - pub(crate) _custom_execute: PhantomData, -} - -// This is a signal, the implementations are in other files -impl< + pub config: Cw721Config< 'a, TMetadata, TCustomResponseMessage, TExtensionExecuteMsg, TMetadataResponse, TCollectionInfoExtension, - > Cw721 - for Cw721Contract< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > -where - TMetadata: Serialize + DeserializeOwned + Clone, - TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, -{ + >, } impl< @@ -92,126 +49,8 @@ where TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { - Self::new( - "collection_info", // Note: do not use deprecated/legacy key "nft_info" - "num_tokens", - "operators", - "nft", // Note: do not use deprecated/legacy key "tokens" - "nft__owner", // Note: do not use deprecated/legacy key "tokens__owner" - "withdraw_address", - ) - } -} - -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > - Cw721Contract< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > -where - TMetadata: Serialize + DeserializeOwned + Clone, - TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, -{ - fn new( - collection_info_key: &'a str, - token_count_key: &'a str, - operator_key: &'a str, - nft_info_key: &'a str, - nft_info_owner_key: &'a str, - withdraw_address_key: &'a str, - ) -> Self { - let indexes = TokenIndexes { - owner: MultiIndex::new(token_owner_idx, nft_info_key, nft_info_owner_key), - }; Self { - collection_info: Item::new(collection_info_key), - token_count: Item::new(token_count_key), - operators: Map::new(operator_key), - nft_info: IndexedMap::new(nft_info_key, indexes), - withdraw_address: Item::new(withdraw_address_key), - _custom_response: PhantomData, - _custom_execute: PhantomData, - _custom_query: PhantomData, + config: Cw721Config::default(), } } - - pub fn token_count(&self, storage: &dyn Storage) -> StdResult { - Ok(self.token_count.may_load(storage)?.unwrap_or_default()) - } - - pub fn increment_tokens(&self, storage: &mut dyn Storage) -> StdResult { - let val = self.token_count(storage)? + 1; - self.token_count.save(storage, &val)?; - Ok(val) - } - - pub fn decrement_tokens(&self, storage: &mut dyn Storage) -> StdResult { - let val = self.token_count(storage)? - 1; - self.token_count.save(storage, &val)?; - Ok(val) - } -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct NftInfo { - /// The owner of the newly minted NFT - pub owner: Addr, - /// Approvals are stored here, as we clear them all upon transfer and cannot accumulate much - pub approvals: Vec, - - /// Universal resource identifier for this NFT - /// Should point to a JSON file that conforms to the ERC721 - /// Metadata JSON Schema - pub token_uri: Option, - - /// You can add any custom metadata here when you extend cw721-base - pub extension: TMetadata, -} - -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -pub struct Approval { - /// Account that can transfer/send the token - pub spender: Addr, - /// When the Approval expires (maybe Expiration::never) - pub expires: Expiration, -} - -impl Approval { - pub fn is_expired(&self, block: &BlockInfo) -> bool { - self.expires.is_expired(block) - } -} - -pub struct TokenIndexes<'a, TMetadata> -where - TMetadata: Serialize + DeserializeOwned + Clone, -{ - pub owner: MultiIndex<'a, Addr, NftInfo, String>, -} - -impl<'a, TMetadata> IndexList> for TokenIndexes<'a, TMetadata> -where - TMetadata: Serialize + DeserializeOwned + Clone, -{ - fn get_indexes(&'_ self) -> Box>> + '_> { - let v: Vec<&dyn Index>> = vec![&self.owner]; - Box::new(v.into_iter()) - } -} - -pub fn token_owner_idx(_pk: &[u8], d: &NftInfo) -> Addr { - d.owner.clone() } diff --git a/contracts/cw721-expiration/examples/schema.rs b/contracts/cw721-expiration/examples/schema.rs index 2c5ee717e..08836096e 100644 --- a/contracts/cw721-expiration/examples/schema.rs +++ b/contracts/cw721-expiration/examples/schema.rs @@ -1,11 +1,11 @@ use cosmwasm_schema::write_api; -use cw721::EmptyCollectionInfoExtension; -use cw721_expiration::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cosmwasm_std::Empty; +use cw721::state::DefaultOptionCollectionInfoExtension; +use cw721_expiration::msg::{InstantiateMsg, QueryMsg}; fn main() { write_api! { - instantiate: InstantiateMsg, - execute: ExecuteMsg, - query: QueryMsg, + instantiate: InstantiateMsg, + query: QueryMsg, } } diff --git a/contracts/cw721-expiration/schema/cw721-expiration.json b/contracts/cw721-expiration/schema/cw721-expiration.json index f03faf79b..e5b5fa814 100644 --- a/contracts/cw721-expiration/schema/cw721-expiration.json +++ b/contracts/cw721-expiration/schema/cw721-expiration.json @@ -15,7 +15,7 @@ "collection_info_extension": { "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" }, { "type": "null" @@ -59,63 +59,123 @@ }, "additionalProperties": false, "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - } - } - }, - "execute": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ExecuteMsg", - "description": "This is like Cw721ExecuteMsg but we add a Mint command for an owner to make this stand-alone. You will likely want to remove mint and use other control logic in any contract that inherits this.", - "oneOf": [ - { - "description": "Transfer is a base message to move a token to another account without triggering actions", + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { "type": "object", "required": [ - "transfer_nft" + "description", + "image" ], "properties": { - "transfer_nft": { - "type": "object", - "required": [ - "recipient", - "token_id" - ], - "properties": { - "recipient": { - "type": "string" + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" }, - "token_id": { - "type": "string" + { + "type": "null" } - }, - "additionalProperties": false + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" } }, "additionalProperties": false }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "execute": null, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ { - "description": "Send is a base message to transfer a token to a contract and trigger an action on the receiving contract.", + "description": "Return the owner of the given token, error if token does not exist", "type": "object", "required": [ - "send_nft" + "owner_of" ], "properties": { - "send_nft": { + "owner_of": { "type": "object", "required": [ - "contract", - "msg", "token_id" ], "properties": { - "contract": { - "type": "string" + "include_expired": { + "description": "unset or false will filter out expired approvals, you must set to true to see them", + "type": [ + "boolean", + "null" + ] }, - "msg": { - "$ref": "#/definitions/Binary" + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] }, "token_id": { "type": "string" @@ -127,27 +187,30 @@ "additionalProperties": false }, { - "description": "Allows operator to transfer / send the token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "description": "Return operator that can access all of the owner's tokens.", "type": "object", "required": [ - "approve" + "approval" ], "properties": { - "approve": { + "approval": { "type": "object", "required": [ "spender", "token_id" ], "properties": { - "expires": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" ] }, "spender": { @@ -163,21 +226,30 @@ "additionalProperties": false }, { - "description": "Remove previously granted Approval", + "description": "Return approvals that a token has", "type": "object", "required": [ - "revoke" + "approvals" ], "properties": { - "revoke": { + "approvals": { "type": "object", "required": [ - "spender", "token_id" ], "properties": { - "spender": { - "type": "string" + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] }, "token_id": { "type": "string" @@ -189,29 +261,26 @@ "additionalProperties": false }, { - "description": "Allows operator to transfer / send any token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", "type": "object", "required": [ - "approve_all" + "nft_info" ], "properties": { - "approve_all": { + "nft_info": { "type": "object", "required": [ - "operator" + "token_id" ], "properties": { - "expires": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" ] }, - "operator": { + "token_id": { "type": "string" } }, @@ -221,19 +290,33 @@ "additionalProperties": false }, { - "description": "Remove previously granted ApproveAll permission", + "description": "With MetaData Extension. Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization for clients", "type": "object", "required": [ - "revoke_all" + "all_nft_info" ], "properties": { - "revoke_all": { + "all_nft_info": { "type": "object", "required": [ - "operator" + "token_id" ], "properties": { - "operator": { + "include_expired": { + "description": "unset or false will filter out expired approvals, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "token_id": { "type": "string" } }, @@ -243,40 +326,37 @@ "additionalProperties": false }, { - "description": "Mint a new NFT, can only be called by the contract minter", + "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", "type": "object", "required": [ - "mint" + "tokens" ], "properties": { - "mint": { + "tokens": { "type": "object", "required": [ - "owner", - "token_id" + "owner" ], "properties": { - "extension": { - "description": "Any custom extension used by this contract", - "anyOf": [ - { - "$ref": "#/definitions/Empty" - }, - { - "type": "null" - } + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" ] }, - "owner": { - "description": "The owner of the newly minter NFT", - "type": "string" + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 }, - "token_id": { - "description": "Unique ID of the NFT", + "owner": { "type": "string" }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", + "start_after": { "type": [ "string", "null" @@ -289,20 +369,35 @@ "additionalProperties": false }, { - "description": "Burn an NFT the sender has access to", + "description": "With Enumerable extension. Requires pagination. Lists all token_ids controlled by the contract.", "type": "object", "required": [ - "burn" + "all_tokens" ], "properties": { - "burn": { + "all_tokens": { "type": "object", - "required": [ - "token_id" - ], "properties": { - "token_id": { - "type": "string" + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] } }, "additionalProperties": false @@ -311,20 +406,30 @@ "additionalProperties": false }, { - "description": "Extension msg", + "description": "Return approval of a given operator for all tokens of an owner, error if not set", "type": "object", "required": [ - "extension" + "operator" ], "properties": { - "extension": { + "operator": { "type": "object", "required": [ - "msg" + "operator", + "owner" ], "properties": { - "msg": { - "$ref": "#/definitions/Empty" + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "operator": { + "type": "string" + }, + "owner": { + "type": "string" } }, "additionalProperties": false @@ -333,20 +438,41 @@ "additionalProperties": false }, { - "description": "Sets address to send withdrawn fees to. Only owner can call this.", + "description": "List all operators that can access all of the owner's tokens", "type": "object", "required": [ - "set_withdraw_address" + "all_operators" ], "properties": { - "set_withdraw_address": { + "all_operators": { "type": "object", "required": [ - "address" + "owner" ], "properties": { - "address": { + "include_expired": { + "description": "unset or false will filter out expired items, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] } }, "additionalProperties": false @@ -355,13 +481,13 @@ "additionalProperties": false }, { - "description": "Removes the withdraw address, so fees are sent to the contract. Only owner can call this.", + "description": "Total number of tokens issued, including all expired NFTs", "type": "object", "required": [ - "remove_withdraw_address" + "num_tokens" ], "properties": { - "remove_withdraw_address": { + "num_tokens": { "type": "object", "additionalProperties": false } @@ -369,320 +495,103 @@ "additionalProperties": false }, { - "description": "Withdraw from the contract to the given address. Anyone can call this, which is okay since withdraw address has been set by owner.", + "deprecated": true, "type": "object", "required": [ - "withdraw_funds" + "contract_info" ], "properties": { - "withdraw_funds": { + "contract_info": { "type": "object", - "required": [ - "amount" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Coin" - } - }, "additionalProperties": false } }, "additionalProperties": false }, { - "description": "Update the contract's ownership. The `action` to be provided can be either to propose transferring ownership to an account, accept a pending ownership transfer, or renounce the ownership permanently.", + "description": "With MetaData Extension. Returns top-level metadata about the contract", "type": "object", "required": [ - "update_ownership" + "get_collection_info" ], "properties": { - "update_ownership": { - "$ref": "#/definitions/Action" - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Action": { - "description": "Actions that can be taken to alter the contract's ownership", - "oneOf": [ - { - "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", + "get_collection_info": { "type": "object", - "required": [ - "transfer_ownership" - ], - "properties": { - "transfer_ownership": { - "type": "object", - "required": [ - "new_owner" - ], - "properties": { - "expiry": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "new_owner": { - "type": "string" - } - }, - "additionalProperties": false - } - }, "additionalProperties": false - }, - { - "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", - "type": "string", - "enum": [ - "accept_ownership" - ] - }, - { - "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", - "type": "string", - "enum": [ - "renounce_ownership" - ] } - ] - }, - "Binary": { - "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", - "type": "string" + }, + "additionalProperties": false }, - "Coin": { + { + "deprecated": true, "type": "object", "required": [ - "amount", - "denom" + "ownership" ], "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", + "ownership": { "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, "additionalProperties": false } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" + }, + "additionalProperties": false }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "query": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", - "oneOf": [ { - "description": "Return the owner of the given token, error if token does not exist", "type": "object", "required": [ - "owner_of" + "get_minter_ownership" ], "properties": { - "owner_of": { + "get_minter_ownership": { "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "include_invalid": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, "additionalProperties": false } }, "additionalProperties": false }, { - "description": "Return operator that can access all of the owner's tokens.", "type": "object", "required": [ - "approval" + "get_creator_ownership" ], "properties": { - "approval": { + "get_creator_ownership": { "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "include_invalid": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, "additionalProperties": false } }, "additionalProperties": false }, { - "description": "Return approvals that a token has", + "description": "Return the minter", + "deprecated": true, "type": "object", "required": [ - "approvals" + "minter" ], "properties": { - "approvals": { + "minter": { "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "include_invalid": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, "additionalProperties": false } }, "additionalProperties": false }, { - "description": "Return approval of a given operator for all tokens of an owner, error if not set", + "description": "Extension query", "type": "object", "required": [ - "operator" + "extension" ], "properties": { - "operator": { + "extension": { "type": "object", "required": [ - "operator", - "owner" + "msg" ], "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "operator": { - "type": "string" - }, - "owner": { - "type": "string" + "msg": { + "$ref": "#/definitions/Empty" } }, "additionalProperties": false @@ -691,40 +600,23 @@ "additionalProperties": false }, { - "description": "List all operators that can access all of the owner's tokens", + "description": "This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg`", "type": "object", "required": [ - "all_operators" + "get_collection_info_extension" ], "properties": { - "all_operators": { + "get_collection_info_extension": { "type": "object", - "required": [ - "owner" - ], "properties": { - "include_expired": { - "description": "unset or false will filter out expired items, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" + "msg": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } ] } }, @@ -734,247 +626,107 @@ "additionalProperties": false }, { - "description": "Total number of tokens issued, including all expired NFTs", - "type": "object", - "required": [ - "num_tokens" - ], - "properties": { - "num_tokens": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "deprecated": true, "type": "object", "required": [ - "contract_info" + "get_withdraw_address" ], "properties": { - "contract_info": { + "get_withdraw_address": { "type": "object", "additionalProperties": false } }, "additionalProperties": false + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" }, - { - "description": "With MetaData Extension. Returns top-level metadata about the contract", + "CollectionInfoExtension_for_RoyaltyInfo": { "type": "object", "required": [ - "collection_info" + "description", + "image" ], "properties": { - "collection_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", - "type": "object", - "required": [ - "nft_info" - ], - "properties": { - "nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_invalid": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" }, - "token_id": { - "type": "string" + { + "type": "null" } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization for clients", - "type": "object", - "required": [ - "all_nft_info" - ], - "properties": { - "all_nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "include_invalid": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" }, - "token_id": { - "type": "string" + { + "type": "null" } - }, - "additionalProperties": false + ] } }, "additionalProperties": false }, - { - "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "include_invalid": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" }, - { - "description": "With Enumerable extension. Requires pagination. Lists all token_ids controlled by the contract.", - "type": "object", - "required": [ - "all_tokens" - ], - "properties": { - "all_tokens": { - "type": "object", - "properties": { - "include_invalid": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" }, - { - "description": "Return the minter", + "RoyaltyInfo": { "type": "object", "required": [ - "minter" + "payment_address", + "share" ], "properties": { - "minter": { - "type": "object", - "additionalProperties": false + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" } }, "additionalProperties": false }, - { - "description": "Extension query", - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "type": "object", - "required": [ - "msg" - ], - "properties": { - "msg": { - "$ref": "#/definitions/Empty" - } - }, - "additionalProperties": false + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" } - }, - "additionalProperties": false + ] }, - { - "description": "Query the contract's ownership information", - "type": "object", - "required": [ - "ownership" - ], - "properties": { - "ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" } } }, @@ -983,7 +735,7 @@ "responses": { "all_nft_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AllNftInfoResponse_for_Nullable_Empty", + "title": "AllNftInfoResponse_for_Nullable_Metadata", "type": "object", "required": [ "access", @@ -1002,13 +754,17 @@ "description": "Data on the token itself,", "allOf": [ { - "$ref": "#/definitions/NftInfoResponse_for_Nullable_Empty" + "$ref": "#/definitions/NftInfoResponse_for_Nullable_Metadata" } ] } }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -1026,15 +782,15 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", "oneOf": [ @@ -1082,14 +838,77 @@ } ] }, - "NftInfoResponse_for_Nullable_Empty": { + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "NftInfoResponse_for_Nullable_Metadata": { "type": "object", "properties": { "extension": { "description": "You can add any custom metadata here when you extend cw721-base", "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/Metadata" }, { "type": "null" @@ -1124,8 +943,261 @@ "description": "Owner of the token", "type": "string" } - }, - "additionalProperties": false + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "all_operators": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "OperatorsResponse", + "type": "object", + "required": [ + "operators" + ], + "properties": { + "operators": { + "type": "array", + "items": { + "$ref": "#/definitions/Approval" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Approval": { + "type": "object", + "required": [ + "expires", + "spender" + ], + "properties": { + "expires": { + "description": "When the Approval expires (maybe Expiration::never)", + "allOf": [ + { + "$ref": "#/definitions/Expiration" + } + ] + }, + "spender": { + "description": "Account that can transfer/send the token", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "all_tokens": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TokensResponse", + "type": "object", + "required": [ + "tokens" + ], + "properties": { + "tokens": { + "description": "Contains all token_ids in lexicographical ordering If there are more than `limit`, use `start_after` in future queries to achieve pagination.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "approval": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ApprovalResponse", + "type": "object", + "required": [ + "approval" + ], + "properties": { + "approval": { + "$ref": "#/definitions/Approval" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Approval": { + "type": "object", + "required": [ + "expires", + "spender" + ], + "properties": { + "expires": { + "description": "When the Approval expires (maybe Expiration::never)", + "allOf": [ + { + "$ref": "#/definitions/Expiration" + } + ] + }, + "spender": { + "description": "Account that can transfer/send the token", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] }, "Timestamp": { "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", @@ -1141,15 +1213,15 @@ } } }, - "all_operators": { + "approvals": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OperatorsResponse", + "title": "ApprovalsResponse", "type": "object", "required": [ - "operators" + "approvals" ], "properties": { - "operators": { + "approvals": { "type": "array", "items": { "$ref": "#/definitions/Approval" @@ -1158,6 +1230,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -1175,7 +1251,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false @@ -1241,60 +1321,298 @@ } } }, - "all_tokens": { + "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "TokensResponse", + "title": "CollectionInfo_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo", "type": "object", "required": [ - "tokens" + "name", + "symbol", + "updated_at" ], "properties": { - "tokens": { - "description": "Contains all token_ids in lexicographical ordering If there are more than `limit`, use `start_after` in future queries to achieve pagination.", - "type": "array", - "items": { - "type": "string" - } + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } }, - "approval": { + "extension": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ApprovalResponse", + "title": "Null", + "type": "null" + }, + "get_collection_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfo_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo", "type": "object", "required": [ - "approval" + "name", + "symbol", + "updated_at" ], "properties": { - "approval": { - "$ref": "#/definitions/Approval" + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" } }, "additionalProperties": false, "definitions": { - "Approval": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { "type": "object", "required": [ - "expires", - "spender" + "description", + "image" ], "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ { - "$ref": "#/definitions/Expiration" + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" } ] }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" } }, "additionalProperties": false }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "get_collection_info_extension": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Null", + "type": "null" + }, + "get_creator_ownership": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Ownership_for_Addr", + "description": "The contract's ownership info", + "type": "object", + "properties": { + "owner": { + "description": "The contract's current owner. `None` if the ownership has been renounced.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "pending_expiry": { + "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "pending_owner": { + "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", "oneOf": [ @@ -1356,44 +1674,51 @@ } } }, - "approvals": { + "get_minter_ownership": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ApprovalsResponse", + "title": "Ownership_for_Addr", + "description": "The contract's ownership info", "type": "object", - "required": [ - "approvals" - ], "properties": { - "approvals": { - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } + "owner": { + "description": "The contract's current owner. `None` if the ownership has been renounced.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "pending_expiry": { + "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "pending_owner": { + "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] } }, "additionalProperties": false, "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", @@ -1456,110 +1781,13 @@ } } }, - "collection_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Nullable_Empty", - "type": "object", - "required": [ - "name", - "symbol", - "updated_at" - ], - "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/Empty" - }, - { - "type": "null" - } - ] - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "contract_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Nullable_Empty", - "type": "object", - "required": [ - "name", - "symbol", - "updated_at" - ], - "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/Empty" - }, - { - "type": "null" - } - ] - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "extension": { + "get_withdraw_address": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Null", - "type": "null" + "title": "Nullable_String", + "type": [ + "string", + "null" + ] }, "minter": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -1578,14 +1806,14 @@ }, "nft_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NftInfoResponse_for_Nullable_Empty", + "title": "NftInfoResponse_for_Nullable_Metadata", "type": "object", "properties": { "extension": { "description": "You can add any custom metadata here when you extend cw721-base", "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/Metadata" }, { "type": "null" @@ -1602,9 +1830,90 @@ }, "additionalProperties": false, "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false } } }, @@ -1638,6 +1947,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -1655,7 +1968,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false @@ -1744,6 +2061,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -1761,7 +2082,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false @@ -1829,15 +2154,19 @@ }, "ownership": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Ownership_for_String", + "title": "Ownership_for_Addr", "description": "The contract's ownership info", "type": "object", "properties": { "owner": { "description": "The contract's current owner. `None` if the ownership has been renounced.", - "type": [ - "string", - "null" + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } ] }, "pending_expiry": { @@ -1853,14 +2182,22 @@ }, "pending_owner": { "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", - "type": [ - "string", - "null" + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } ] } }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", "oneOf": [ diff --git a/contracts/cw721-expiration/src/contract_tests.rs b/contracts/cw721-expiration/src/contract_tests.rs index 10cd8a88e..039ff49b7 100644 --- a/contracts/cw721-expiration/src/contract_tests.rs +++ b/contracts/cw721-expiration/src/contract_tests.rs @@ -1,38 +1,59 @@ #![cfg(test)] +use std::ops::Add; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{ - from_json, to_json_binary, Addr, CosmosMsg, DepsMut, Response, StdError, WasmMsg, + from_json, to_json_binary, Addr, CosmosMsg, DepsMut, Empty, Response, StdError, WasmMsg, }; -use cw721::{ - Approval, ApprovalResponse, CollectionInfo, Cw721ReceiveMsg, Expiration, NftInfoResponse, - OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, +use cw721::error::Cw721ContractError; +use cw721::msg::Cw721ExecuteMsg; +use cw721::query::{ + ApprovalResponse, NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, + TokensResponse, }; +use cw721::receiver::Cw721ReceiveMsg; +use cw721::state::{CollectionInfo, DefaultOptionCollectionInfoExtension, CREATOR, MINTER}; +use cw721::{execute::Cw721Execute, query::Cw721Query, Approval, Expiration}; use cw_ownable::{Action, Ownership, OwnershipError}; use crate::state::Cw721ExpirationContract; use crate::{ - error::ContractError, msg::ExecuteMsg, msg::InstantiateMsg, msg::QueryMsg, EmptyExtension, - MinterResponse, + error::ContractError, msg::InstantiateMsg, msg::QueryMsg, DefaultOptionMetadataExtension, }; -use cw721_base::ContractError as Cw721ContractError; -const MINTER: &str = "minter"; -const CREATOR: &str = "creator"; +const MINTER_ADDR: &str = "minter"; +const CREATOR_ADDR: &str = "creator"; const CONTRACT_NAME: &str = "Magic Power"; const SYMBOL: &str = "MGK"; -fn setup_contract(deps: DepsMut<'_>, expiration_days: u16) -> Cw721ExpirationContract<'static> { - let contract = Cw721ExpirationContract::default(); +fn setup_contract( + deps: DepsMut<'_>, + expiration_days: u16, +) -> Cw721ExpirationContract< + 'static, + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, +> { + let contract = Cw721ExpirationContract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); let msg = InstantiateMsg { expiration_days, name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), collection_info_extension: None, - minter: Some(String::from(MINTER)), - creator: Some(String::from(CREATOR)), + minter: Some(String::from(MINTER_ADDR)), + creator: Some(String::from(CREATOR_ADDR)), withdraw_address: None, }; let info = mock_info("creator", &[]); @@ -44,16 +65,22 @@ fn setup_contract(deps: DepsMut<'_>, expiration_days: u16) -> Cw721ExpirationCon #[test] fn proper_instantiation() { let mut deps = mock_dependencies(); - let contract = Cw721ExpirationContract::default(); + let contract = Cw721ExpirationContract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + Option, + >::default(); let msg = InstantiateMsg { expiration_days: 1, name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - collection_info_extension: None, - minter: Some(String::from(MINTER)), - creator: Some(String::from(CREATOR)), - withdraw_address: None, + collection_info_extension: Some(Empty {}), + minter: Some(String::from(MINTER_ADDR)), + creator: Some(String::from(CREATOR_ADDR)), + withdraw_address: Some(String::from(CREATOR_ADDR)), }; let info = mock_info("creator", &[]); let env = mock_env(); @@ -65,25 +92,110 @@ fn proper_instantiation() { assert_eq!(0, res.messages.len()); // it worked, let's query the state - let res = contract.minter(deps.as_ref()).unwrap(); - assert_eq!(Some(MINTER.to_string()), res.minter); - let info = contract.contract_info(deps.as_ref()).unwrap(); + let minter_ownership = MINTER.get_ownership(deps.as_ref().storage).unwrap(); + assert_eq!(Some(Addr::unchecked(MINTER_ADDR)), minter_ownership.owner); + let creator_ownership = CREATOR.get_ownership(deps.as_ref().storage).unwrap(); + assert_eq!(Some(Addr::unchecked(CREATOR_ADDR)), creator_ownership.owner); + let collection_info = contract + .base_contract + .query_collection_info(deps.as_ref(), env.clone()) + .unwrap(); assert_eq!( - info, + collection_info, CollectionInfo { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - extension: None, + extension: Some(Empty {}), + updated_at: env.block.time, + } + ); + + let withdraw_address = contract + .base_contract + .config + .withdraw_address + .may_load(deps.as_ref().storage) + .unwrap(); + assert_eq!(Some(CREATOR_ADDR.to_string()), withdraw_address); + + let count = contract + .base_contract + .query_num_tokens(deps.as_ref(), env.clone()) + .unwrap(); + assert_eq!(0, count.count); + + // list the token_ids + let tokens = contract + .query_all_tokens_include_expired_nft(deps.as_ref(), mock_env(), None, None, false) + .unwrap(); + assert_eq!(0, tokens.tokens.len()); +} + +#[test] +fn proper_instantiation_with_collection_info() { + let mut deps = mock_dependencies(); + let contract = Cw721ExpirationContract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + Option, + >::default(); + + let msg = InstantiateMsg { + expiration_days: 1, + name: CONTRACT_NAME.to_string(), + symbol: SYMBOL.to_string(), + collection_info_extension: Some(Empty {}), + minter: Some(String::from(MINTER_ADDR)), + creator: Some(String::from(CREATOR_ADDR)), + withdraw_address: Some(String::from(CREATOR_ADDR)), + }; + let info = mock_info("creator", &[]); + let env = mock_env(); + + // we can just call .unwrap() to assert this was a success + let res = contract + .instantiate(deps.as_mut(), env.clone(), info, msg) + .unwrap(); + assert_eq!(0, res.messages.len()); + + // it worked, let's query the state + let minter_ownership = MINTER.get_ownership(deps.as_ref().storage).unwrap(); + assert_eq!(Some(Addr::unchecked(MINTER_ADDR)), minter_ownership.owner); + let creator_ownership = CREATOR.get_ownership(deps.as_ref().storage).unwrap(); + assert_eq!(Some(Addr::unchecked(CREATOR_ADDR)), creator_ownership.owner); + let collection_info = contract + .base_contract + .query_collection_info(deps.as_ref(), env.clone()) + .unwrap(); + assert_eq!( + collection_info, + CollectionInfo { + name: CONTRACT_NAME.to_string(), + symbol: SYMBOL.to_string(), + extension: Some(Empty {}), updated_at: env.block.time, } ); - let count = contract.num_tokens(deps.as_ref()).unwrap(); + let withdraw_address = contract + .base_contract + .config + .withdraw_address + .may_load(deps.as_ref().storage) + .unwrap(); + assert_eq!(Some(CREATOR_ADDR.to_string()), withdraw_address); + + let count = contract + .base_contract + .query_num_tokens(deps.as_ref(), env.clone()) + .unwrap(); assert_eq!(0, count.count); // list the token_ids let tokens = contract - .all_tokens(deps.as_ref(), mock_env(), None, None, false) + .query_all_tokens_include_expired_nft(deps.as_ref(), mock_env(), None, None, false) .unwrap(); assert_eq!(0, tokens.tokens.len()); } @@ -96,7 +208,7 @@ fn test_mint() { let token_id = "atomize".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/atomize".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: String::from("medusa"), token_uri: Some(token_uri.clone()), @@ -105,8 +217,9 @@ fn test_mint() { // random cannot mint let random = mock_info("random", &[]); + let env = mock_env(); let err = contract - .execute(deps.as_mut(), mock_env(), random, mint_msg.clone()) + .execute(deps.as_mut(), env.clone(), random, mint_msg.clone()) .unwrap_err(); assert_eq!( err, @@ -114,27 +227,30 @@ fn test_mint() { ); // minter can mint - let allowed = mock_info(MINTER, &[]); + let allowed = mock_info(MINTER_ADDR, &[]); let _ = contract .execute(deps.as_mut(), mock_env(), allowed, mint_msg) .unwrap(); // ensure num tokens increases - let count = contract.num_tokens(deps.as_ref()).unwrap(); + let count = contract + .base_contract + .query_num_tokens(deps.as_ref(), env.clone()) + .unwrap(); assert_eq!(1, count.count); // unknown nft returns error let _ = contract - .nft_info(deps.as_ref(), mock_env(), "unknown".to_string(), false) + .query_nft_info_include_expired_nft(deps.as_ref(), mock_env(), "unknown".to_string(), false) .unwrap_err(); // this nft info is correct let info = contract - .nft_info(deps.as_ref(), mock_env(), token_id.clone(), false) + .query_nft_info_include_expired_nft(deps.as_ref(), mock_env(), token_id.clone(), false) .unwrap(); assert_eq!( info, - NftInfoResponse:: { + NftInfoResponse:: { token_uri: Some(token_uri), extension: None, } @@ -142,7 +258,13 @@ fn test_mint() { // owner info is correct let owner = contract - .owner_of(deps.as_ref(), mock_env(), token_id.clone(), true, false) + .query_owner_of_include_expired_nft( + deps.as_ref(), + mock_env(), + token_id.clone(), + true, + false, + ) .unwrap(); assert_eq!( owner, @@ -160,14 +282,14 @@ fn test_mint() { assert_eq!(mint_timestamp, mock_env().block.time); // Cannot mint same token_id again - let mint_msg2 = ExecuteMsg::Mint { + let mint_msg2 = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: String::from("hercules"), token_uri: None, extension: None, }; - let allowed = mock_info(MINTER, &[]); + let allowed = mock_info(MINTER_ADDR, &[]); let err = contract .execute(deps.as_mut(), mock_env(), allowed, mint_msg2) .unwrap_err(); @@ -175,7 +297,7 @@ fn test_mint() { // list the token_ids let tokens = contract - .all_tokens(deps.as_ref(), mock_env(), None, None, false) + .query_all_tokens_include_expired_nft(deps.as_ref(), mock_env(), None, None, false) .unwrap(); assert_eq!(1, tokens.tokens.len()); assert_eq!(vec![token_id], tokens.tokens); @@ -189,7 +311,7 @@ fn test_update_minter() { let token_id = "petrify".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/petrify".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id, owner: String::from("medusa"), token_uri: Some(token_uri.clone()), @@ -197,7 +319,7 @@ fn test_update_minter() { }; // Minter can mint - let minter_info = mock_info(MINTER, &[]); + let minter_info = mock_info(MINTER_ADDR, &[]); let _ = contract .execute(deps.as_mut(), mock_env(), minter_info.clone(), mint_msg) .unwrap(); @@ -209,7 +331,7 @@ fn test_update_minter() { deps.as_mut(), mock_env(), minter_info.clone(), - ExecuteMsg::UpdateMinterOwnership(Action::TransferOwnership { + Cw721ExecuteMsg::UpdateMinterOwnership(Action::TransferOwnership { new_owner: "random".to_string(), expiry: None, }), @@ -217,14 +339,6 @@ fn test_update_minter() { .unwrap(); // Minter does not change until ownership transfer completes. - let minter_ownership: Ownership = from_json( - contract - .query(deps.as_ref(), mock_env(), QueryMsg::GetMinterOwnership {}) - .unwrap(), - ) - .unwrap(); - assert_eq!(minter_ownership.owner, Some(minter_info.sender.clone())); - // Pending ownership transfer should be discoverable via query. let ownership: Ownership = from_json( contract @@ -236,7 +350,7 @@ fn test_update_minter() { assert_eq!( ownership, Ownership:: { - owner: Some(Addr::unchecked(MINTER)), + owner: Some(Addr::unchecked(MINTER_ADDR)), pending_owner: Some(Addr::unchecked("random")), pending_expiry: None, } @@ -249,7 +363,7 @@ fn test_update_minter() { deps.as_mut(), mock_env(), random_info.clone(), - ExecuteMsg::UpdateMinterOwnership(Action::AcceptOwnership), + Cw721ExecuteMsg::UpdateMinterOwnership(Action::AcceptOwnership), ) .unwrap(); @@ -262,7 +376,7 @@ fn test_update_minter() { .unwrap(); assert_eq!(minter_ownership.owner, Some(random_info.sender.clone())); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: "randoms_token".to_string(), owner: String::from("medusa"), token_uri: Some(token_uri), @@ -292,20 +406,20 @@ fn test_burn() { let token_id = "petrify".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/petrify".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), - owner: MINTER.to_string(), + owner: MINTER_ADDR.to_string(), token_uri: Some(token_uri), extension: None, }; - let burn_msg = ExecuteMsg::Burn { + let burn_msg = Cw721ExecuteMsg::Burn { token_id: token_id.clone(), }; // mint some NFT let mut env = mock_env(); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); let _ = contract .execute(deps.as_mut(), env.clone(), minter.clone(), mint_msg.clone()) .unwrap(); @@ -326,17 +440,25 @@ fn test_burn() { .unwrap(); // ensure num tokens decreases - let count = contract.num_tokens(deps.as_ref()).unwrap(); + let count = contract + .base_contract + .query_num_tokens(deps.as_ref(), env.clone()) + .unwrap(); assert_eq!(0, count.count); // trying to get nft returns error let _ = contract - .nft_info(deps.as_ref(), env.clone(), "petrify".to_string(), false) + .query_nft_info_include_expired_nft( + deps.as_ref(), + env.clone(), + "petrify".to_string(), + false, + ) .unwrap_err(); // list the token_ids let tokens = contract - .all_tokens(deps.as_ref(), env.clone(), None, None, false) + .query_all_tokens_include_expired_nft(deps.as_ref(), env.clone(), None, None, false) .unwrap(); assert!(tokens.tokens.is_empty()); @@ -372,7 +494,7 @@ fn test_transfer_nft() { let token_uri = "https://www.merriam-webster.com/dictionary/melt".to_string(); let owner = "owner"; - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: String::from(owner), token_uri: Some(token_uri), @@ -380,14 +502,14 @@ fn test_transfer_nft() { }; let mut env = mock_env(); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); contract .execute(deps.as_mut(), env.clone(), minter, mint_msg) .unwrap(); // random cannot transfer let random = mock_info("random", &[]); - let transfer_msg = ExecuteMsg::TransferNft { + let transfer_msg = Cw721ExecuteMsg::TransferNft { recipient: String::from("random"), token_id: token_id.clone(), }; @@ -403,7 +525,7 @@ fn test_transfer_nft() { // owner can let owner_info = mock_info(owner, &[]); let new_owner = "random"; - let transfer_msg = ExecuteMsg::TransferNft { + let transfer_msg = Cw721ExecuteMsg::TransferNft { recipient: String::from(new_owner), token_id: token_id.clone(), }; @@ -452,7 +574,7 @@ fn test_send_nft() { let token_id = "melt".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/melt".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: String::from("venus"), token_uri: Some(token_uri), @@ -460,14 +582,14 @@ fn test_send_nft() { }; let mut env = mock_env(); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); contract .execute(deps.as_mut(), env.clone(), minter, mint_msg) .unwrap(); let msg = to_json_binary("You now have the melting power").unwrap(); let target = String::from("another_contract"); - let send_msg = ExecuteMsg::SendNft { + let send_msg = Cw721ExecuteMsg::SendNft { contract: target.clone(), token_id: token_id.clone(), msg: msg.clone(), @@ -538,7 +660,7 @@ fn test_approve_revoke() { let token_id = "grow".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/grow".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: String::from("demeter"), token_uri: Some(token_uri), @@ -546,14 +668,14 @@ fn test_approve_revoke() { }; let mut env = mock_env(); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); contract .execute(deps.as_mut(), env.clone(), minter, mint_msg) .unwrap(); // token owner shows in approval query let res = contract - .approval( + .query_approval_include_expired_nft( deps.as_ref(), env.clone(), token_id.clone(), @@ -566,14 +688,14 @@ fn test_approve_revoke() { res, ApprovalResponse { approval: Approval { - spender: String::from("demeter"), + spender: Addr::unchecked("demeter"), expires: Expiration::Never {} } } ); // Give random transferring power - let approve_msg = ExecuteMsg::Approve { + let approve_msg = Cw721ExecuteMsg::Approve { spender: String::from("random"), token_id: token_id.clone(), expires: None, @@ -593,7 +715,7 @@ fn test_approve_revoke() { // test approval query let res = contract - .approval( + .query_approval_include_expired_nft( deps.as_ref(), env.clone(), token_id.clone(), @@ -606,7 +728,7 @@ fn test_approve_revoke() { res, ApprovalResponse { approval: Approval { - spender: String::from("random"), + spender: Addr::unchecked("random"), expires: Expiration::Never {} } } @@ -614,7 +736,7 @@ fn test_approve_revoke() { // random can now transfer let random = mock_info("random", &[]); - let transfer_msg = ExecuteMsg::TransferNft { + let transfer_msg = Cw721ExecuteMsg::TransferNft { recipient: String::from("person"), token_id: token_id.clone(), }; @@ -626,7 +748,7 @@ fn test_approve_revoke() { let query_msg = QueryMsg::OwnerOf { token_id: token_id.clone(), include_expired: None, - include_invalid: None, + include_expired_nft: None, }; let res: OwnerOfResponse = from_json( contract @@ -643,7 +765,7 @@ fn test_approve_revoke() { ); // Approve, revoke, and check for empty, to test revoke - let approve_msg = ExecuteMsg::Approve { + let approve_msg = Cw721ExecuteMsg::Approve { spender: String::from("random"), token_id: token_id.clone(), expires: None, @@ -658,7 +780,7 @@ fn test_approve_revoke() { ) .unwrap(); - let revoke_msg = ExecuteMsg::Revoke { + let revoke_msg = Cw721ExecuteMsg::Revoke { spender: String::from("random"), token_id: token_id.clone(), }; @@ -728,19 +850,19 @@ fn test_approve_all_revoke_all() { let token_id2 = "grow2".to_string(); let token_uri2 = "https://www.merriam-webster.com/dictionary/grow2".to_string(); - let mint_msg1 = ExecuteMsg::Mint { + let mint_msg1 = Cw721ExecuteMsg::Mint { token_id: token_id1.clone(), owner: String::from("demeter"), token_uri: Some(token_uri1), extension: None, }; - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); contract .execute(deps.as_mut(), mock_env(), minter.clone(), mint_msg1) .unwrap(); - let mint_msg2 = ExecuteMsg::Mint { + let mint_msg2 = Cw721ExecuteMsg::Mint { token_id: token_id2.clone(), owner: String::from("demeter"), token_uri: Some(token_uri2), @@ -753,12 +875,12 @@ fn test_approve_all_revoke_all() { // paginate the token_ids let tokens = contract - .all_tokens(deps.as_ref(), mock_env(), None, Some(1), false) + .query_all_tokens_include_expired_nft(deps.as_ref(), mock_env(), None, Some(1), false) .unwrap(); assert_eq!(1, tokens.tokens.len()); assert_eq!(vec![token_id1.clone()], tokens.tokens); let tokens = contract - .all_tokens( + .query_all_tokens_include_expired_nft( deps.as_ref(), mock_env(), Some(token_id1.clone()), @@ -770,7 +892,7 @@ fn test_approve_all_revoke_all() { assert_eq!(vec![token_id2.clone()], tokens.tokens); // demeter gives random full (operator) power over her tokens - let approve_all_msg = ExecuteMsg::ApproveAll { + let approve_all_msg = Cw721ExecuteMsg::ApproveAll { operator: String::from("random"), expires: None, }; @@ -788,7 +910,7 @@ fn test_approve_all_revoke_all() { // random can now transfer let random = mock_info("random", &[]); - let transfer_msg = ExecuteMsg::TransferNft { + let transfer_msg = Cw721ExecuteMsg::TransferNft { recipient: String::from("person"), token_id: token_id1, }; @@ -804,7 +926,7 @@ fn test_approve_all_revoke_all() { }; let msg: CosmosMsg = CosmosMsg::Wasm(inner_msg); - let send_msg = ExecuteMsg::SendNft { + let send_msg = Cw721ExecuteMsg::SendNft { contract: String::from("another_contract"), token_id: token_id2, msg: to_json_binary(&msg).unwrap(), @@ -814,7 +936,7 @@ fn test_approve_all_revoke_all() { .unwrap(); // Approve_all, revoke_all, and check for empty, to test revoke_all - let approve_all_msg = ExecuteMsg::ApproveAll { + let approve_all_msg = Cw721ExecuteMsg::ApproveAll { operator: String::from("operator"), expires: None, }; @@ -826,7 +948,8 @@ fn test_approve_all_revoke_all() { // query for operator should return approval let res = contract - .operator( + .base_contract + .query_operator( deps.as_ref(), mock_env(), String::from("person"), @@ -838,14 +961,14 @@ fn test_approve_all_revoke_all() { res, OperatorResponse { approval: Approval { - spender: String::from("operator"), + spender: Addr::unchecked("operator"), expires: Expiration::Never {} } } ); // query for other should throw error - let res = contract.operator( + let res = contract.base_contract.query_operator( deps.as_ref(), mock_env(), String::from("person"), @@ -858,7 +981,8 @@ fn test_approve_all_revoke_all() { } let res = contract - .operators( + .base_contract + .query_operators( deps.as_ref(), mock_env(), String::from("person"), @@ -871,7 +995,7 @@ fn test_approve_all_revoke_all() { res, OperatorsResponse { operators: vec![cw721::Approval { - spender: String::from("operator"), + spender: Addr::unchecked("operator"), expires: Expiration::Never {} }] } @@ -879,7 +1003,7 @@ fn test_approve_all_revoke_all() { // second approval let buddy_expires = Expiration::AtHeight(1234567); - let approve_all_msg = ExecuteMsg::ApproveAll { + let approve_all_msg = Cw721ExecuteMsg::ApproveAll { operator: String::from("buddy"), expires: Some(buddy_expires), }; @@ -890,7 +1014,8 @@ fn test_approve_all_revoke_all() { // and paginate queries let res = contract - .operators( + .base_contract + .query_operators( deps.as_ref(), mock_env(), String::from("person"), @@ -903,13 +1028,14 @@ fn test_approve_all_revoke_all() { res, OperatorsResponse { operators: vec![cw721::Approval { - spender: String::from("buddy"), + spender: Addr::unchecked("buddy"), expires: buddy_expires, }] } ); let res = contract - .operators( + .base_contract + .query_operators( deps.as_ref(), mock_env(), String::from("person"), @@ -922,13 +1048,13 @@ fn test_approve_all_revoke_all() { res, OperatorsResponse { operators: vec![cw721::Approval { - spender: String::from("operator"), + spender: Addr::unchecked("operator"), expires: Expiration::Never {} }] } ); - let revoke_all_msg = ExecuteMsg::RevokeAll { + let revoke_all_msg = Cw721ExecuteMsg::RevokeAll { operator: String::from("operator"), }; contract @@ -936,7 +1062,7 @@ fn test_approve_all_revoke_all() { .unwrap(); // query for operator should return error - let res = contract.operator( + let res = contract.base_contract.query_operator( deps.as_ref(), mock_env(), String::from("person"), @@ -950,7 +1076,8 @@ fn test_approve_all_revoke_all() { // Approvals are removed / cleared without affecting others let res = contract - .operators( + .base_contract + .query_operators( deps.as_ref(), mock_env(), String::from("person"), @@ -963,7 +1090,7 @@ fn test_approve_all_revoke_all() { res, OperatorsResponse { operators: vec![cw721::Approval { - spender: String::from("buddy"), + spender: Addr::unchecked("buddy"), expires: buddy_expires, }] } @@ -973,7 +1100,8 @@ fn test_approve_all_revoke_all() { let mut late_env = mock_env(); late_env.block.height = 1234568; //expired let res = contract - .operators( + .base_contract + .query_operators( deps.as_ref(), late_env.clone(), String::from("person"), @@ -985,7 +1113,7 @@ fn test_approve_all_revoke_all() { assert_eq!(0, res.operators.len()); // query operator should also return error - let res = contract.operator( + let res = contract.base_contract.query_operator( deps.as_ref(), late_env, String::from("person"), @@ -1003,7 +1131,7 @@ fn test_approve_all_revoke_all() { fn test_tokens_by_owner() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut(), 1); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); // Mint a couple tokens (from the same owner) let token_id1 = "grow1".to_string(); @@ -1012,7 +1140,7 @@ fn test_tokens_by_owner() { let ceres = String::from("ceres"); let token_id3 = "sing".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id1.clone(), owner: demeter.clone(), token_uri: None, @@ -1022,7 +1150,7 @@ fn test_tokens_by_owner() { .execute(deps.as_mut(), mock_env(), minter.clone(), mint_msg) .unwrap(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id2.clone(), owner: ceres.clone(), token_uri: None, @@ -1032,7 +1160,7 @@ fn test_tokens_by_owner() { .execute(deps.as_mut(), mock_env(), minter.clone(), mint_msg) .unwrap(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id3.clone(), owner: demeter.clone(), token_uri: None, @@ -1045,16 +1173,16 @@ fn test_tokens_by_owner() { // get all tokens in order: let expected = vec![token_id1.clone(), token_id2.clone(), token_id3.clone()]; let tokens = contract - .all_tokens(deps.as_ref(), mock_env(), None, None, false) + .query_all_tokens_include_expired_nft(deps.as_ref(), mock_env(), None, None, false) .unwrap(); assert_eq!(&expected, &tokens.tokens); // paginate let tokens = contract - .all_tokens(deps.as_ref(), mock_env(), None, Some(2), false) + .query_all_tokens_include_expired_nft(deps.as_ref(), mock_env(), None, Some(2), false) .unwrap(); assert_eq!(&expected[..2], &tokens.tokens[..]); let tokens = contract - .all_tokens( + .query_all_tokens_include_expired_nft( deps.as_ref(), mock_env(), Some(expected[1].clone()), @@ -1069,7 +1197,7 @@ fn test_tokens_by_owner() { let by_demeter = vec![token_id1, token_id3]; // all tokens by owner let tokens = contract - .tokens( + .query_tokens_include_expired_nft( deps.as_ref(), mock_env(), demeter.clone(), @@ -1080,13 +1208,13 @@ fn test_tokens_by_owner() { .unwrap(); assert_eq!(&by_demeter, &tokens.tokens); let tokens = contract - .tokens(deps.as_ref(), mock_env(), ceres, None, None, false) + .query_tokens_include_expired_nft(deps.as_ref(), mock_env(), ceres, None, None, false) .unwrap(); assert_eq!(&by_ceres, &tokens.tokens); // paginate for demeter let tokens = contract - .tokens( + .query_tokens_include_expired_nft( deps.as_ref(), mock_env(), demeter.clone(), @@ -1097,7 +1225,7 @@ fn test_tokens_by_owner() { .unwrap(); assert_eq!(&by_demeter[..1], &tokens.tokens[..]); let tokens = contract - .tokens( + .query_tokens_include_expired_nft( deps.as_ref(), mock_env(), demeter, @@ -1113,13 +1241,13 @@ fn test_tokens_by_owner() { fn test_nft_info() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut(), 1); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); let token_id = "grow1".to_string(); let owner = String::from("ark"); let mut env = mock_env(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner, token_uri: None, @@ -1131,7 +1259,7 @@ fn test_nft_info() { // assert valid nft is returned contract - .nft_info(deps.as_ref(), env.clone(), token_id.clone(), false) + .query_nft_info_include_expired_nft(deps.as_ref(), env.clone(), token_id.clone(), false) .unwrap(); // assert invalid nft throws error @@ -1139,7 +1267,7 @@ fn test_nft_info() { let expiration = env.block.time.plus_days(1); env.block.time = expiration; let error = contract - .nft_info(deps.as_ref(), env, token_id.clone(), false) + .query_nft_info_include_expired_nft(deps.as_ref(), env, token_id.clone(), false) .unwrap_err(); assert_eq!( error, @@ -1155,13 +1283,13 @@ fn test_nft_info() { fn test_all_nft_info() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut(), 1); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); let token_id = "grow1".to_string(); let owner = String::from("ark"); let mut env = mock_env(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner, token_uri: None, @@ -1173,7 +1301,13 @@ fn test_all_nft_info() { // assert valid nft is returned contract - .all_nft_info(deps.as_ref(), env.clone(), token_id.clone(), false, false) + .query_all_nft_info_include_expired_nft( + deps.as_ref(), + env.clone(), + token_id.clone(), + false, + false, + ) .unwrap(); // assert invalid nft throws error @@ -1181,7 +1315,7 @@ fn test_all_nft_info() { let expiration = env.block.time.plus_days(1); env.block.time = expiration; let error = contract - .all_nft_info(deps.as_ref(), env, token_id.clone(), false, false) + .query_all_nft_info_include_expired_nft(deps.as_ref(), env, token_id.clone(), false, false) .unwrap_err(); assert_eq!( error, @@ -1197,13 +1331,13 @@ fn test_all_nft_info() { fn test_owner_of() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut(), 1); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); let token_id = "grow1".to_string(); let owner = String::from("ark"); let mut env = mock_env(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner, token_uri: None, @@ -1215,7 +1349,13 @@ fn test_owner_of() { // assert valid nft is returned contract - .owner_of(deps.as_ref(), env.clone(), token_id.clone(), false, false) + .query_owner_of_include_expired_nft( + deps.as_ref(), + env.clone(), + token_id.clone(), + false, + false, + ) .unwrap(); // assert invalid nft throws error @@ -1223,7 +1363,7 @@ fn test_owner_of() { let expiration = env.block.time.plus_days(1); env.block.time = expiration; let error = contract - .owner_of(deps.as_ref(), env, token_id.clone(), false, false) + .query_owner_of_include_expired_nft(deps.as_ref(), env, token_id.clone(), false, false) .unwrap_err(); assert_eq!( error, @@ -1239,13 +1379,13 @@ fn test_owner_of() { fn test_approval() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut(), 1); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); let token_id = "grow1".to_string(); let owner = String::from("ark"); let mut env = mock_env(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: owner.clone(), token_uri: None, @@ -1257,7 +1397,7 @@ fn test_approval() { // assert valid nft is returned contract - .approval( + .query_approval_include_expired_nft( deps.as_ref(), env.clone(), token_id.clone(), @@ -1272,7 +1412,14 @@ fn test_approval() { let expiration = env.block.time.plus_days(1); env.block.time = expiration; let error = contract - .approval(deps.as_ref(), env, token_id.clone(), owner, false, false) + .query_approval_include_expired_nft( + deps.as_ref(), + env, + token_id.clone(), + owner, + false, + false, + ) .unwrap_err(); assert_eq!( error, @@ -1288,13 +1435,13 @@ fn test_approval() { fn test_approvals() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut(), 1); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); let token_id = "grow1".to_string(); let owner = String::from("ark"); let mut env = mock_env(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner, token_uri: None, @@ -1306,7 +1453,13 @@ fn test_approvals() { // assert valid nft is returned contract - .approvals(deps.as_ref(), env.clone(), token_id.clone(), false, false) + .query_approvals_include_expired_nft( + deps.as_ref(), + env.clone(), + token_id.clone(), + false, + false, + ) .unwrap(); // assert invalid nft throws error @@ -1314,7 +1467,7 @@ fn test_approvals() { let expiration = env.block.time.plus_days(1); env.block.time = expiration; let error = contract - .approvals(deps.as_ref(), env, token_id.clone(), false, false) + .query_approvals_include_expired_nft(deps.as_ref(), env, token_id.clone(), false, false) .unwrap_err(); assert_eq!( error, @@ -1330,13 +1483,13 @@ fn test_approvals() { fn test_tokens() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut(), 1); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); let token_id = "grow1".to_string(); let owner = String::from("ark"); let mut env = mock_env(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: owner.clone(), token_uri: None, @@ -1348,20 +1501,34 @@ fn test_tokens() { // assert valid nft is returned contract - .tokens(deps.as_ref(), env.clone(), owner.clone(), None, None, false) + .query_tokens_include_expired_nft( + deps.as_ref(), + env.clone(), + owner.clone(), + None, + None, + false, + ) .unwrap(); // assert invalid nft is not returned let expiration = env.block.time.plus_days(1); env.block.time = expiration; let tokens = contract - .tokens(deps.as_ref(), env.clone(), owner.clone(), None, None, false) + .query_tokens_include_expired_nft( + deps.as_ref(), + env.clone(), + owner.clone(), + None, + None, + false, + ) .unwrap(); assert_eq!(tokens, TokensResponse { tokens: vec![] }); // assert invalid nft is returned let tokens = contract - .tokens(deps.as_ref(), env, owner, None, None, true) + .query_tokens_include_expired_nft(deps.as_ref(), env, owner, None, None, true) .unwrap(); assert_eq!( tokens, @@ -1375,13 +1542,13 @@ fn test_tokens() { fn test_all_tokens() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut(), 1); - let minter = mock_info(MINTER, &[]); + let minter = mock_info(MINTER_ADDR, &[]); let token_id = "grow1".to_string(); let owner = String::from("ark"); let mut env = mock_env(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: owner.clone(), token_uri: None, @@ -1393,20 +1560,20 @@ fn test_all_tokens() { // assert valid nft is returned contract - .all_tokens(deps.as_ref(), env.clone(), None, None, false) + .query_all_tokens_include_expired_nft(deps.as_ref(), env.clone(), None, None, false) .unwrap(); // assert invalid nft is not returned let expiration = env.block.time.plus_days(1); env.block.time = expiration; let tokens = contract - .tokens(deps.as_ref(), env.clone(), owner, None, None, false) + .query_tokens_include_expired_nft(deps.as_ref(), env.clone(), owner, None, None, false) .unwrap(); assert_eq!(tokens, TokensResponse { tokens: vec![] }); // assert invalid nft is returned let tokens = contract - .all_tokens(deps.as_ref(), env, None, None, true) + .query_all_tokens_include_expired_nft(deps.as_ref(), env, None, None, true) .unwrap(); assert_eq!( tokens, diff --git a/contracts/cw721-expiration/src/error.rs b/contracts/cw721-expiration/src/error.rs index bf041df59..211c6ceef 100644 --- a/contracts/cw721-expiration/src/error.rs +++ b/contracts/cw721-expiration/src/error.rs @@ -1,5 +1,5 @@ use cosmwasm_std::Timestamp; -use cw721_base::error::ContractError as Cw721ContractError; +use cw721::error::Cw721ContractError; use thiserror::Error; #[derive(Error, Debug, PartialEq)] diff --git a/contracts/cw721-expiration/src/execute.rs b/contracts/cw721-expiration/src/execute.rs index 1d6f1efc0..f03c95b3f 100644 --- a/contracts/cw721-expiration/src/execute.rs +++ b/contracts/cw721-expiration/src/execute.rs @@ -1,30 +1,65 @@ use cosmwasm_std::{ - Addr, Binary, Coin, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, Storage, + Binary, CustomMsg, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, }; -use cw721::{Cw721Execute, EmptyCollectionInfoExtension, Expiration}; -use cw721_base::Cw721Contract; -use cw_ownable::Action; +use cw721::{ + execute::Cw721Execute, + msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}, + state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, + Expiration, +}; +use serde::de::DeserializeOwned; +use serde::Serialize; use crate::{ - error::ContractError, msg::ExecuteMsg, msg::InstantiateMsg, state::Cw721ExpirationContract, - EmptyExtension, + error::ContractError, msg::InstantiateMsg, state::Cw721ExpirationContract, CONTRACT_NAME, + CONTRACT_VERSION, }; -use cw721_base::InstantiateMsg as Cw721InstantiateMsg; -impl<'a> Cw721ExpirationContract<'a> { +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > + Cw721ExpirationContract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > +where + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ + // -- instantiate -- pub fn instantiate( &self, deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, - ) -> Result { + msg: InstantiateMsg, + ) -> Result, ContractError> { if msg.expiration_days == 0 { return Err(ContractError::MinExpiration {}); } - self.expiration_days + let contract = Cw721ExpirationContract::< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + >::default(); + contract + .expiration_days .save(deps.storage, &msg.expiration_days)?; - Ok(self.base_contract.instantiate( + Ok(contract.base_contract.instantiate( deps, env, info, @@ -36,70 +71,63 @@ impl<'a> Cw721ExpirationContract<'a> { creator: msg.creator, withdraw_address: msg.withdraw_address, }, + CONTRACT_NAME, + CONTRACT_VERSION, )?) } + // -- execute -- pub fn execute( &self, deps: DepsMut, env: Env, info: MessageInfo, - msg: ExecuteMsg, - ) -> Result { + msg: Cw721ExecuteMsg, + ) -> Result, ContractError> { + let contract = Cw721ExpirationContract::< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + >::default(); match msg { - ExecuteMsg::Mint { + Cw721ExecuteMsg::Mint { token_id, owner, token_uri, extension, - } => self.mint(deps, env, info, token_id, owner, token_uri, extension), - ExecuteMsg::Approve { + } => { + contract.mint_with_timestamp(deps, env, info, token_id, owner, token_uri, extension) + } + Cw721ExecuteMsg::Approve { spender, token_id, expires, - } => self.approve(deps, env, info, spender, token_id, expires), - ExecuteMsg::Revoke { spender, token_id } => { - self.revoke(deps, env, info, spender, token_id) - } - ExecuteMsg::ApproveAll { operator, expires } => { - self.approve_all(deps, env, info, operator, expires) + } => contract.approve_include_nft_expired(deps, env, info, spender, token_id, expires), + Cw721ExecuteMsg::Revoke { spender, token_id } => { + contract.revoke_include_nft_expired(deps, env, info, spender, token_id) } - ExecuteMsg::RevokeAll { operator } => self.revoke_all(deps, env, info, operator), - ExecuteMsg::TransferNft { + Cw721ExecuteMsg::TransferNft { recipient, token_id, - } => self.transfer_nft(deps, env, info, recipient, token_id), - ExecuteMsg::SendNft { - contract, + } => contract.transfer_nft_include_nft_expired(deps, env, info, recipient, token_id), + Cw721ExecuteMsg::SendNft { + contract: recipient, token_id, msg, - } => self.send_nft(deps, env, info, contract, token_id, msg), - ExecuteMsg::Burn { token_id } => self.burn(deps, env, info, token_id), - #[allow(deprecated)] - ExecuteMsg::UpdateOwnership(action) => { - Self::update_minter_ownership(deps, env, info, action) - } - ExecuteMsg::UpdateMinterOwnership(action) => { - Self::update_minter_ownership(deps, env, info, action) - } - ExecuteMsg::UpdateCreatorOwnership(action) => { - Self::update_creator_ownership(deps, env, info, action) + } => contract.send_nft_include_nft_expired(deps, env, info, recipient, token_id, msg), + Cw721ExecuteMsg::Burn { token_id } => { + contract.burn_nft_include_nft_expired(deps, env, info, token_id) } - ExecuteMsg::Extension { msg: _ } => Ok(Response::default()), - ExecuteMsg::SetWithdrawAddress { address } => { - self.set_withdraw_address(deps, &info.sender, address) + _ => { + let response = contract.base_contract.execute(deps, env, info, msg)?; + Ok(response) } - ExecuteMsg::RemoveWithdrawAddress {} => { - self.remove_withdraw_address(deps.storage, &info.sender) - } - ExecuteMsg::WithdrawFunds { amount } => self.withdraw_funds(deps.storage, &amount), } } -} -impl<'a> Cw721ExpirationContract<'a> { - #[allow(clippy::too_many_arguments)] - pub fn mint( + pub fn mint_with_timestamp( &self, deps: DepsMut, env: Env, @@ -107,8 +135,8 @@ impl<'a> Cw721ExpirationContract<'a> { token_id: String, owner: String, token_uri: Option, - extension: EmptyExtension, - ) -> Result { + extension: TMetadata, + ) -> Result, ContractError> { let mint_timstamp = env.block.time; self.mint_timestamps .save(deps.storage, &token_id, &mint_timstamp)?; @@ -119,98 +147,7 @@ impl<'a> Cw721ExpirationContract<'a> { Ok(res) } - pub fn update_minter_ownership( - deps: DepsMut, - env: Env, - info: MessageInfo, - action: Action, - ) -> Result { - Ok(Cw721Contract::< - EmptyExtension, - Empty, - Empty, - Empty, - EmptyCollectionInfoExtension, - >::update_minter_ownership(deps, env, info, action)?) - } - - pub fn update_creator_ownership( - deps: DepsMut, - env: Env, - info: MessageInfo, - action: Action, - ) -> Result { - Ok(Cw721Contract::< - EmptyExtension, - Empty, - Empty, - Empty, - EmptyCollectionInfoExtension, - >::update_creator_ownership(deps, env, info, action)?) - } - - pub fn set_withdraw_address( - &self, - deps: DepsMut, - sender: &Addr, - address: String, - ) -> Result { - Ok(self - .base_contract - .set_withdraw_address(deps, sender, address)?) - } - - pub fn remove_withdraw_address( - &self, - storage: &mut dyn Storage, - sender: &Addr, - ) -> Result { - Ok(self - .base_contract - .remove_withdraw_address(storage, sender)?) - } - - pub fn withdraw_funds( - &self, - storage: &mut dyn Storage, - amount: &Coin, - ) -> Result { - Ok(self.base_contract.withdraw_funds(storage, amount)?) - } -} - -// execute -impl<'a> Cw721ExpirationContract<'a> { - fn transfer_nft( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - recipient: String, - token_id: String, - ) -> Result, ContractError> { - self.assert_valid_nft(deps.as_ref(), &env, &token_id)?; - Ok(self - .base_contract - .transfer_nft(deps, env, info, recipient, token_id)?) - } - - fn send_nft( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - contract: String, - token_id: String, - msg: Binary, - ) -> Result, ContractError> { - self.assert_valid_nft(deps.as_ref(), &env, &token_id)?; - Ok(self - .base_contract - .send_nft(deps, env, info, contract, token_id, msg)?) - } - - fn approve( + pub fn approve_include_nft_expired( &self, deps: DepsMut, env: Env, @@ -218,94 +155,64 @@ impl<'a> Cw721ExpirationContract<'a> { spender: String, token_id: String, expires: Option, - ) -> Result, ContractError> { - self.assert_valid_nft(deps.as_ref(), &env, &token_id)?; + ) -> Result, ContractError> { + self.assert_nft_expired(deps.as_ref(), &env, token_id.as_str())?; Ok(self .base_contract .approve(deps, env, info, spender, token_id, expires)?) } - fn revoke( + pub fn revoke_include_nft_expired( &self, deps: DepsMut, env: Env, info: MessageInfo, spender: String, token_id: String, - ) -> Result, ContractError> { - self.assert_valid_nft(deps.as_ref(), &env, &token_id)?; + ) -> Result, ContractError> { + self.assert_nft_expired(deps.as_ref(), &env, token_id.as_str())?; Ok(self .base_contract .revoke(deps, env, info, spender, token_id)?) } - fn approve_all( + pub fn transfer_nft_include_nft_expired( &self, deps: DepsMut, env: Env, info: MessageInfo, - operator: String, - expires: Option, - ) -> Result { + recipient: String, + token_id: String, + ) -> Result, ContractError> { + self.assert_nft_expired(deps.as_ref(), &env, token_id.as_str())?; Ok(self .base_contract - .approve_all(deps, env, info, operator, expires)?) + .transfer_nft(deps, env, info, recipient, token_id)?) } - fn revoke_all( + pub fn send_nft_include_nft_expired( &self, deps: DepsMut, env: Env, info: MessageInfo, - operator: String, - ) -> Result { - Ok(self.base_contract.revoke_all(deps, env, info, operator)?) + contract: String, + token_id: String, + msg: Binary, + ) -> Result, ContractError> { + self.assert_nft_expired(deps.as_ref(), &env, token_id.as_str())?; + Ok(self + .base_contract + .send_nft(deps, env, info, contract, token_id, msg)?) } - fn burn( + pub fn burn_nft_include_nft_expired( &self, deps: DepsMut, env: Env, info: MessageInfo, token_id: String, - ) -> Result { - self.assert_valid_nft(deps.as_ref(), &env, &token_id)?; - Ok(self.base_contract.burn(deps, env, info, token_id)?) - } -} - -// helpers -impl<'a> Cw721ExpirationContract<'a> { - /// throws contract error if nft is expired - pub fn is_valid_nft(&self, deps: Deps, env: &Env, token_id: &str) -> StdResult { - // any non-expired token approval can send - let mint_date = self.mint_timestamps.load(deps.storage, token_id)?; - let expiration_days = self.expiration_days.load(deps.storage)?; - let expiration = mint_date.plus_days(expiration_days.into()); - if env.block.time >= expiration { - return Ok(false); - } - Ok(true) - } - - /// throws contract error if nft is expired - pub fn assert_valid_nft( - &self, - deps: Deps, - env: &Env, - token_id: &str, - ) -> Result<(), ContractError> { - // any non-expired token approval can send - let mint_date = self.mint_timestamps.load(deps.storage, token_id)?; - let expiration_days = self.expiration_days.load(deps.storage)?; - let expiration = mint_date.plus_days(expiration_days.into()); - if env.block.time >= expiration { - return Err(ContractError::NftExpired { - token_id: token_id.to_string(), - mint_date, - expiration, - }); - } - Ok(()) + ) -> Result, ContractError> { + self.assert_nft_expired(deps.as_ref(), &env, token_id.as_str())?; + Ok(self.base_contract.burn_nft(deps, env, info, token_id)?) } } diff --git a/contracts/cw721-expiration/src/lib.rs b/contracts/cw721-expiration/src/lib.rs index 260bd2572..d744bdcb2 100644 --- a/contracts/cw721-expiration/src/lib.rs +++ b/contracts/cw721-expiration/src/lib.rs @@ -8,20 +8,21 @@ pub mod state; mod contract_tests; use cosmwasm_std::Empty; +use cw721::state::DefaultOptionMetadataExtension; +use cw721::{execute::Cw721Execute, query::Cw721Query}; // Version info for migration const CONTRACT_NAME: &str = "crates.io:cw721-expiration"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -pub type MinterResponse = cw721_base::msg::MinterResponse; -pub use cw721_base::{EmptyCollectionInfoExtension, EmptyExtension}; +pub type MinterResponse = cw721::msg::MinterResponse; -pub type NftInfo = cw721_base::state::NftInfo; +pub type NftInfo = cw721::state::NftInfo; pub mod entry { use crate::{ error::ContractError, - msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + msg::{InstantiateMsg, QueryMsg}, state::Cw721ExpirationContract, }; @@ -29,7 +30,11 @@ pub mod entry { #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; - use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response}; + use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; + use cw721::{ + msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}, + state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, + }; // This makes a conscious choice on the various generics used by the contract #[cfg_attr(not(feature = "library"), entry_point)] @@ -37,10 +42,16 @@ pub mod entry { mut deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - Cw721ExpirationContract::default().instantiate(deps.branch(), env, info, msg) + let contract = Cw721ExpirationContract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + contract.instantiate(deps, env, info, msg) } #[entry_point] @@ -48,19 +59,42 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: ExecuteMsg, + msg: Cw721ExecuteMsg< + DefaultOptionMetadataExtension, + Empty, + DefaultOptionCollectionInfoExtension, + >, ) -> Result { - Cw721ExpirationContract::default().execute(deps, env, info, msg) + let contract = Cw721ExpirationContract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + contract.execute(deps, env, info, msg) } #[entry_point] - pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { - Cw721ExpirationContract::default().query(deps, env, msg) + pub fn query( + deps: Deps, + env: Env, + msg: QueryMsg, + ) -> Result { + let contract = Cw721ExpirationContract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + contract.query(deps, env, msg) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> Result { - Ok(Response::default()) + // TODO: allow migration e.g. from cw721-base + panic!("This contract does not support migrations") } } @@ -68,6 +102,7 @@ pub mod entry { mod tests { use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cw2::ContractVersion; + use cw721::state::DefaultOptionCollectionInfoExtension; use crate::{error::ContractError, msg::InstantiateMsg, state::Cw721ExpirationContract}; @@ -123,10 +158,16 @@ mod tests { assert_eq!( 1, - Cw721ExpirationContract::default() - .expiration_days - .load(deps.as_ref().storage) - .unwrap() + Cw721ExpirationContract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default() + .expiration_days + .load(deps.as_ref().storage) + .unwrap() ); } } diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index 7d892aec8..5b827c18f 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -1,8 +1,9 @@ -use crate::{EmptyExtension, MinterResponse}; +use crate::{DefaultOptionMetadataExtension, MinterResponse}; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, Empty}; +use cw721::state::CollectionInfo; use cw_ownable::Ownership; -pub type ExecuteMsg = cw721_base::ExecuteMsg; +use schemars::JsonSchema; #[cw_serde] pub struct InstantiateMsg { @@ -30,112 +31,117 @@ pub struct InstantiateMsg { #[cw_serde] #[derive(QueryResponses)] -pub enum QueryMsg { +pub enum QueryMsg { + // -------- below adds `include_expired_nft` prop to cw721/src/msg.rs -------- /// Return the owner of the given token, error if token does not exist - #[returns(cw721::OwnerOfResponse)] + #[returns(cw721::query::OwnerOfResponse)] OwnerOf { token_id: String, /// unset or false will filter out expired approvals, you must set to true to see them include_expired: Option, /// unset or false will filter out expired nfts, you must set to true to see them - include_invalid: Option, + include_expired_nft: Option, }, /// Return operator that can access all of the owner's tokens. - #[returns(cw721::ApprovalResponse)] + #[returns(cw721::query::ApprovalResponse)] Approval { token_id: String, spender: String, include_expired: Option, /// unset or false will filter out expired nfts, you must set to true to see them - include_invalid: Option, + include_expired_nft: Option, }, /// Return approvals that a token has - #[returns(cw721::ApprovalsResponse)] + #[returns(cw721::query::ApprovalsResponse)] Approvals { token_id: String, include_expired: Option, /// unset or false will filter out expired nfts, you must set to true to see them - include_invalid: Option, + include_expired_nft: Option, }, - /// Return approval of a given operator for all tokens of an owner, error if not set - #[returns(cw721::OperatorResponse)] - Operator { - owner: String, - operator: String, - include_expired: Option, - }, - /// List all operators that can access all of the owner's tokens - #[returns(cw721::OperatorsResponse)] - AllOperators { - owner: String, - /// unset or false will filter out expired items, you must set to true to see them - include_expired: Option, - start_after: Option, - limit: Option, - }, - /// Total number of tokens issued, including all expired NFTs - #[returns(cw721::NumTokensResponse)] - NumTokens {}, - - #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] - #[returns(cw721::CollectionInfo)] - ContractInfo {}, - - /// With MetaData Extension. - /// Returns top-level metadata about the contract - #[returns(cw721::CollectionInfo)] - GetCollectionInfo {}, - - #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] - #[returns(Ownership)] - Ownership {}, - - #[returns(Ownership)] - GetMinterOwnership {}, - - #[returns(Ownership)] - GetCreatorOwnership {}, /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract - #[returns(cw721::NftInfoResponse)] + #[returns(cw721::query::NftInfoResponse)] NftInfo { token_id: String, /// unset or false will filter out expired nfts, you must set to true to see them - include_invalid: Option, + include_expired_nft: Option, }, + /// With MetaData Extension. /// Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization /// for clients - #[returns(cw721::AllNftInfoResponse)] + #[returns(cw721::query::AllNftInfoResponse)] AllNftInfo { token_id: String, /// unset or false will filter out expired approvals, you must set to true to see them include_expired: Option, /// unset or false will filter out expired nfts, you must set to true to see them - include_invalid: Option, + include_expired_nft: Option, }, /// With Enumerable extension. /// Returns all tokens owned by the given address, [] if unset. - #[returns(cw721::TokensResponse)] + #[returns(cw721::query::TokensResponse)] Tokens { owner: String, start_after: Option, limit: Option, /// unset or false will filter out expired nfts, you must set to true to see them - include_invalid: Option, + include_expired_nft: Option, }, + /// With Enumerable extension. /// Requires pagination. Lists all token_ids controlled by the contract. - #[returns(cw721::TokensResponse)] + #[returns(cw721::query::TokensResponse)] AllTokens { start_after: Option, limit: Option, /// unset or false will filter out expired nfts, you must set to true to see them - include_invalid: Option, + include_expired_nft: Option, + }, + + // -------- below is from cw721/src/msg.rs -------- + /// Return approval of a given operator for all tokens of an owner, error if not set + #[returns(cw721::query::OperatorResponse)] + Operator { + owner: String, + operator: String, + include_expired: Option, + }, + /// List all operators that can access all of the owner's tokens + #[returns(cw721::query::OperatorsResponse)] + AllOperators { + owner: String, + /// unset or false will filter out expired items, you must set to true to see them + include_expired: Option, + start_after: Option, + limit: Option, }, + /// Total number of tokens issued, including all expired NFTs + #[returns(cw721::query::NumTokensResponse)] + NumTokens {}, + + #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] + #[returns(cw721::state::CollectionInfo)] + ContractInfo {}, + + /// With MetaData Extension. + /// Returns top-level metadata about the contract + #[returns(CollectionInfo)] + GetCollectionInfo {}, + + #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] + #[returns(Ownership)] + Ownership {}, + + #[returns(Ownership)] + GetMinterOwnership {}, + + #[returns(Ownership)] + GetCreatorOwnership {}, /// Return the minter #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] @@ -144,5 +150,13 @@ pub enum QueryMsg { /// Extension query #[returns(())] - Extension { msg: Empty }, + Extension { msg: TMetadataResponse }, + + /// This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: + /// `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg` + #[returns(())] + GetCollectionInfoExtension { msg: TCollectionInfoExtension }, + + #[returns(Option)] + GetWithdrawAddress {}, } diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index 442d2c63c..c7abd6cf5 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -1,69 +1,165 @@ -use cosmwasm_std::{to_json_binary, Addr, Binary, Deps, Env, StdResult, Storage}; -use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721Query, - EmptyCollectionInfoExtension, NftInfoResponse, NumTokensResponse, OperatorResponse, - OperatorsResponse, OwnerOfResponse, TokensResponse, +use cosmwasm_std::{to_json_binary, Addr, Binary, CustomMsg, Deps, Env, StdResult, Storage}; +use cw721::query::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, Cw721Query, NftInfoResponse, + OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; -use cw721_base::MinterResponse; -use cw_ownable::Ownership; +use serde::de::DeserializeOwned; +use serde::Serialize; -use crate::{error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract, EmptyExtension}; +use crate::{ + error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract, + DefaultOptionMetadataExtension, +}; -impl<'a> Cw721ExpirationContract<'a> { - pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> Result { +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > + Cw721ExpirationContract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > +where + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ + pub fn query( + &self, + deps: Deps, + env: Env, + msg: QueryMsg, + ) -> Result { + let contract = Cw721ExpirationContract::< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + >::default(); match msg { - #[allow(deprecated)] - QueryMsg::Minter {} => Ok(to_json_binary(&self.minter(deps)?)?), - QueryMsg::ContractInfo {} => Ok(to_json_binary(&self.collection_info(deps)?)?), - QueryMsg::GetCollectionInfo {} => Ok(to_json_binary(&self.collection_info(deps)?)?), - QueryMsg::NftInfo { + // -------- msgs with `include_expired_nft` prop -------- + QueryMsg::OwnerOf { token_id, - include_invalid, - } => Ok(to_json_binary(&self.nft_info( - deps, - env, + include_expired: include_expired_approval, + include_expired_nft, + } => Ok(to_json_binary( + &contract.query_owner_of_include_expired_nft( + deps, + env, + token_id, + include_expired_approval.unwrap_or(false), + include_expired_nft.unwrap_or(false), + )?, + )?), + QueryMsg::Approval { token_id, - include_invalid.unwrap_or(false), - )?)?), - QueryMsg::OwnerOf { + spender, + include_expired, + include_expired_nft: include_invalid, + } => Ok(to_json_binary( + &contract.query_approval_include_expired_nft( + deps, + env, + token_id, + spender, + include_expired.unwrap_or(false), + include_invalid.unwrap_or(false), + )?, + )?), + QueryMsg::Approvals { token_id, include_expired, - include_invalid, - } => Ok(to_json_binary(&self.owner_of( - deps, - env, + include_expired_nft: include_invalid, + } => Ok(to_json_binary( + &contract.query_approvals_include_expired_nft( + deps, + env, + token_id, + include_expired.unwrap_or(false), + include_invalid.unwrap_or(false), + )?, + )?), + QueryMsg::NftInfo { token_id, - include_expired.unwrap_or(false), - include_invalid.unwrap_or(false), - )?)?), + include_expired_nft: include_expired, + } => Ok(to_json_binary( + &contract.query_nft_info_include_expired_nft( + deps, + env, + token_id, + include_expired.unwrap_or(false), + )?, + )?), QueryMsg::AllNftInfo { token_id, - include_expired, - include_invalid, - } => Ok(to_json_binary(&self.all_nft_info( - deps, - env, - token_id, - include_expired.unwrap_or(false), - include_invalid.unwrap_or(false), - )?)?), + include_expired: include_expired_approval, + include_expired_nft, + } => Ok(to_json_binary( + &contract.query_all_nft_info_include_expired_nft( + deps, + env, + token_id, + include_expired_approval.unwrap_or(false), + include_expired_nft.unwrap_or(false), + )?, + )?), + QueryMsg::Tokens { + owner, + start_after, + limit, + include_expired_nft: include_invalid, + } => Ok(to_json_binary( + &contract.query_tokens_include_expired_nft( + deps, + env, + owner, + start_after, + limit, + include_invalid.unwrap_or(false), + )?, + )?), + QueryMsg::AllTokens { + start_after, + limit, + include_expired_nft: include_invalid, + } => Ok(to_json_binary( + &contract.query_all_tokens_include_expired_nft( + deps, + env, + start_after, + limit, + include_invalid.unwrap_or(false), + )?, + )?), + // -------- below is from cw721/src/msg.rs -------- QueryMsg::Operator { owner, operator, - include_expired, - } => Ok(to_json_binary(&self.operator( + include_expired: include_expired_approval, + } => Ok(to_json_binary(&contract.base_contract.query_operator( deps, env, owner, operator, - include_expired.unwrap_or(false), + include_expired_approval.unwrap_or(false), )?)?), QueryMsg::AllOperators { owner, include_expired, start_after, limit, - } => Ok(to_json_binary(&self.operators( + } => Ok(to_json_binary(&contract.base_contract.query_operators( deps, env, owner, @@ -71,253 +167,214 @@ impl<'a> Cw721ExpirationContract<'a> { start_after, limit, )?)?), - QueryMsg::NumTokens {} => Ok(to_json_binary(&self.num_tokens(deps)?)?), - QueryMsg::Tokens { - owner, - start_after, - limit, - include_invalid, - } => Ok(to_json_binary(&self.tokens( - deps, - env, - owner, - start_after, - limit, - include_invalid.unwrap_or(false), - )?)?), - QueryMsg::AllTokens { - start_after, - limit, - include_invalid, - } => Ok(to_json_binary(&self.all_tokens( - deps, - env, - start_after, - limit, - include_invalid.unwrap_or(false), - )?)?), - QueryMsg::Approval { - token_id, - spender, - include_expired, - include_invalid, - } => Ok(to_json_binary(&self.approval( - deps, - env, - token_id, - spender, - include_expired.unwrap_or(false), - include_invalid.unwrap_or(false), - )?)?), - QueryMsg::Approvals { - token_id, - include_expired, - include_invalid, - } => Ok(to_json_binary(&self.approvals( - deps, - env, - token_id, - include_expired.unwrap_or(false), - include_invalid.unwrap_or(false), - )?)?), + QueryMsg::NumTokens {} => Ok(to_json_binary( + &contract.base_contract.query_num_tokens(deps, env)?, + )?), #[allow(deprecated)] - QueryMsg::Ownership {} => Ok(to_json_binary(&self.minter_ownership(deps.storage)?)?), - QueryMsg::GetMinterOwnership {} => { - Ok(to_json_binary(&self.minter_ownership(deps.storage)?)?) - } - QueryMsg::GetCreatorOwnership {} => { - Ok(to_json_binary(&self.creator_ownership(deps.storage)?)?) - } - - QueryMsg::Extension { msg: _ } => Ok(Binary::default()), + QueryMsg::ContractInfo {} => Ok(to_json_binary( + &contract.base_contract.query_collection_info(deps, env)?, + )?), + QueryMsg::GetCollectionInfo {} => Ok(to_json_binary( + &contract.base_contract.query_collection_info(deps, env)?, + )?), + #[allow(deprecated)] + QueryMsg::Ownership {} => Ok(to_json_binary( + &contract + .base_contract + .query_minter_ownership(deps.storage)?, + )?), + QueryMsg::GetMinterOwnership {} => Ok(to_json_binary( + &contract + .base_contract + .query_minter_ownership(deps.storage)?, + )?), + QueryMsg::GetCreatorOwnership {} => Ok(to_json_binary( + &contract + .base_contract + .query_creator_ownership(deps.storage)?, + )?), + #[allow(deprecated)] + QueryMsg::Minter {} => Ok(to_json_binary( + &contract.base_contract.query_minter(deps.storage)?, + )?), + QueryMsg::Extension { msg } => Ok(to_json_binary( + &contract.base_contract.query_extension(deps, env, msg)?, + )?), + QueryMsg::GetCollectionInfoExtension { msg } => Ok(to_json_binary( + &contract + .base_contract + .query_collection_info_extension(deps, env, msg)?, + )?), + QueryMsg::GetWithdrawAddress {} => Ok(to_json_binary( + &contract.base_contract.query_withdraw_address(deps)?, + )?), } } - #[deprecated(since = "0.19.0", note = "Please use minter_ownership instead")] - pub fn minter(&self, deps: Deps) -> StdResult { - #[allow(deprecated)] - self.base_contract.minter(deps) - } - - pub fn minter_ownership(&self, storage: &dyn Storage) -> StdResult> { - self.base_contract.minter_ownership(storage) - } - - pub fn creator_ownership(&self, storage: &dyn Storage) -> StdResult> { - self.base_contract.creator_ownership(storage) - } -} - -// queries -impl<'a> Cw721ExpirationContract<'a> { - pub fn contract_info( - &self, - deps: Deps, - ) -> StdResult> { - self.base_contract.collection_info(deps) - } - - pub fn collection_info( - &self, - deps: Deps, - ) -> StdResult> { - self.base_contract.collection_info.load(deps.storage) - } - - pub fn num_tokens(&self, deps: Deps) -> StdResult { - self.base_contract.num_tokens(deps) - } - - pub fn nft_info( + pub fn query_nft_info_include_expired_nft( &self, deps: Deps, env: Env, token_id: String, - include_invalid: bool, - ) -> Result, ContractError> { - if !include_invalid { - self.assert_valid_nft(deps, &env, token_id.as_str())?; + include_expired_nft: bool, + ) -> Result, ContractError> { + if !include_expired_nft { + self.assert_nft_expired(deps, &env, token_id.as_str())?; } - Ok(self.base_contract.nft_info(deps, token_id)?) + Ok(self.base_contract.query_nft_info(deps, env, token_id)?) } - pub fn owner_of( + pub fn query_owner_of_include_expired_nft( &self, deps: Deps, env: Env, token_id: String, - include_expired: bool, - include_invalid: bool, + include_expired_approval: bool, + include_expired_nft: bool, ) -> Result { - if !include_invalid { - self.assert_valid_nft(deps, &env, token_id.as_str())?; + if !include_expired_nft { + self.assert_nft_expired(deps, &env, token_id.as_str())?; } Ok(self .base_contract - .owner_of(deps, env, token_id, include_expired)?) + .query_owner_of(deps, env, token_id, include_expired_approval)?) } - /// operator returns the approval status of an operator for a given owner if exists - pub fn operator( - &self, - deps: Deps, - env: Env, - owner: String, - operator: String, - include_expired: bool, - ) -> StdResult { - self.base_contract - .operator(deps, env, owner, operator, include_expired) - } - - /// operators returns all operators owner given access to - pub fn operators( - &self, - deps: Deps, - env: Env, - owner: String, - include_expired: bool, - start_after: Option, - limit: Option, - ) -> StdResult { - self.base_contract - .operators(deps, env, owner, include_expired, start_after, limit) - } - - pub fn approval( + pub fn query_approval_include_expired_nft( &self, deps: Deps, env: Env, token_id: String, spender: String, - include_expired: bool, - include_invalid: bool, + include_expired_approval: bool, + include_expired_nft: bool, ) -> Result { - if !include_invalid { - self.assert_valid_nft(deps, &env, token_id.as_str())?; + if !include_expired_nft { + self.assert_nft_expired(deps, &env, token_id.as_str())?; } - Ok(self - .base_contract - .approval(deps, env, token_id, spender, include_expired)?) + Ok(self.base_contract.query_approval( + deps, + env, + token_id, + spender, + include_expired_approval, + )?) } /// approvals returns all approvals owner given access to - pub fn approvals( + pub fn query_approvals_include_expired_nft( &self, deps: Deps, env: Env, token_id: String, - include_expired: bool, - include_invalid: bool, + include_expired_approval: bool, + include_expired_nft: bool, ) -> Result { - if !include_invalid { - self.assert_valid_nft(deps, &env, token_id.as_str())?; + if !include_expired_nft { + self.assert_nft_expired(deps, &env, token_id.as_str())?; } Ok(self .base_contract - .approvals(deps, env, token_id, include_expired)?) + .query_approvals(deps, env, token_id, include_expired_approval)?) } - pub fn tokens( + pub fn query_tokens_include_expired_nft( &self, deps: Deps, env: Env, owner: String, start_after: Option, limit: Option, - include_invalid: bool, + include_expired_nft: bool, ) -> StdResult { - let tokens = self.base_contract.tokens(deps, owner, start_after, limit)?; - if include_invalid { + let tokens = + self.base_contract + .query_tokens(deps, env.clone(), owner, start_after, limit)?; + if include_expired_nft { return Ok(tokens); } let filtered: Vec<_> = tokens .tokens .iter() .filter(|token_id| { - self.is_valid_nft(deps, &env, token_id).unwrap_or(false) // Convert Option to bool + self.is_nft_expired(deps, &env, token_id).unwrap_or(false) // Convert Option to bool }) .map(|token_id| token_id.to_string()) .collect(); Ok(TokensResponse { tokens: filtered }) } - pub fn all_tokens( + pub fn query_all_tokens_include_expired_nft( &self, deps: Deps, env: Env, start_after: Option, limit: Option, - include_invalid: bool, + include_expired_nft: bool, ) -> Result { - let tokens = self.base_contract.all_tokens(deps, start_after, limit)?; - if include_invalid { + let tokens = self + .base_contract + .query_all_tokens(deps, env.clone(), start_after, limit)?; + if include_expired_nft { return Ok(tokens); } let filtered: Vec<_> = tokens .tokens .iter() .filter(|token_id| { - self.is_valid_nft(deps, &env, token_id).unwrap_or(false) // Convert Option to bool + self.is_nft_expired(deps, &env, token_id).unwrap_or(false) // Convert Option to bool }) .map(|token_id| token_id.to_string()) .collect(); Ok(TokensResponse { tokens: filtered }) } - pub fn all_nft_info( + pub fn query_all_nft_info_include_expired_nft( &self, deps: Deps, env: Env, token_id: String, - include_expired: bool, - include_invalid: bool, - ) -> Result, ContractError> { - if !include_invalid { - self.assert_valid_nft(deps, &env, token_id.as_str())?; + include_expired_approval: bool, + include_expired_nft: bool, + ) -> Result, ContractError> { + if !include_expired_nft { + self.assert_nft_expired(deps, &env, token_id.as_str())?; } Ok(self .base_contract - .all_nft_info(deps, env, token_id, include_expired)?) + .query_all_nft_info(deps, env, token_id, include_expired_approval)?) + } + + // --- helpers --- + pub fn is_nft_expired(&self, deps: Deps, env: &Env, token_id: &str) -> StdResult { + // any non-expired token approval can send + let mint_date = self.mint_timestamps.load(deps.storage, token_id)?; + let expiration_days = self.expiration_days.load(deps.storage)?; + let expiration = mint_date.plus_days(expiration_days.into()); + if env.block.time >= expiration { + return Ok(false); + } + Ok(true) + } + + /// throws contract error if nft is expired + pub fn assert_nft_expired( + &self, + deps: Deps, + env: &Env, + token_id: &str, + ) -> Result<(), ContractError> { + // any non-expired token approval can send + let mint_date = self.mint_timestamps.load(deps.storage, token_id)?; + let expiration_days = self.expiration_days.load(deps.storage)?; + let expiration = mint_date.plus_days(expiration_days.into()); + if env.block.time >= expiration { + return Err(ContractError::NftExpired { + token_id: token_id.to_string(), + mint_date, + expiration, + }); + } + Ok(()) } } diff --git a/contracts/cw721-expiration/src/state.rs b/contracts/cw721-expiration/src/state.rs index 50a94344b..0cb4f3b2f 100644 --- a/contracts/cw721-expiration/src/state.rs +++ b/contracts/cw721-expiration/src/state.rs @@ -1,27 +1,60 @@ -use cosmwasm_std::{Empty, Timestamp}; +use cosmwasm_std::{CustomMsg, Timestamp}; +use cw721_base::Cw721Contract; use cw_storage_plus::{Item, Map}; +use serde::de::DeserializeOwned; +use serde::Serialize; -use crate::{EmptyCollectionInfoExtension, EmptyExtension}; - -pub struct Cw721ExpirationContract<'a> { +pub struct Cw721ExpirationContract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, +> where + TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataResponse: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ pub expiration_days: Item<'a, u16>, // max 65535 days pub mint_timestamps: Map<'a, &'a str, Timestamp>, - pub base_contract: cw721_base::Cw721Contract< + pub base_contract: Cw721Contract< 'a, - EmptyExtension, - Empty, - Empty, - Empty, - EmptyCollectionInfoExtension, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, >, } -impl Default for Cw721ExpirationContract<'static> { +impl< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > Default + for Cw721ExpirationContract< + 'static, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > +where + TMetadata: Serialize + DeserializeOwned + Clone, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ fn default() -> Self { Self { expiration_days: Item::new("expiration_days"), mint_timestamps: Map::new("mint_timestamps"), - base_contract: cw721_base::Cw721Contract::default(), + base_contract: Cw721Contract::default(), } } } diff --git a/contracts/cw721-fixed-price/Cargo.toml b/contracts/cw721-fixed-price/Cargo.toml index f47aeddb7..82f018e28 100644 --- a/contracts/cw721-fixed-price/Cargo.toml +++ b/contracts/cw721-fixed-price/Cargo.toml @@ -22,6 +22,7 @@ cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw2 = { workspace = true } cw20 = { workspace = true } +cw721 = { workspace = true } cw721-base = { workspace = true, features = ["library"] } cw-storage-plus = { workspace = true } cw-utils = { workspace = true } diff --git a/contracts/cw721-fixed-price/examples/schema.rs b/contracts/cw721-fixed-price/examples/schema.rs index 2da660b34..4ad3733e7 100644 --- a/contracts/cw721-fixed-price/examples/schema.rs +++ b/contracts/cw721-fixed-price/examples/schema.rs @@ -1,11 +1,11 @@ use cosmwasm_schema::write_api; -use cw721_base::EmptyCollectionInfoExtension; +use cw721::state::DefaultOptionCollectionInfoExtension; use cw721_fixed_price::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: InstantiateMsg, execute: ExecuteMsg, query: QueryMsg, } diff --git a/contracts/cw721-fixed-price/schema/cw721-fixed-price.json b/contracts/cw721-fixed-price/schema/cw721-fixed-price.json index 69db020ec..251684b7c 100644 --- a/contracts/cw721-fixed-price/schema/cw721-fixed-price.json +++ b/contracts/cw721-fixed-price/schema/cw721-fixed-price.json @@ -20,7 +20,7 @@ "collection_info_extension": { "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" }, { "type": "null" @@ -33,7 +33,7 @@ "extension": { "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/Metadata" }, { "type": "null" @@ -78,13 +78,174 @@ "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", "type": "string" }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" } } }, @@ -190,7 +351,7 @@ "extension": { "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/Metadata" }, { "type": "null" @@ -229,9 +390,90 @@ "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", "type": "string" }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", diff --git a/contracts/cw721-fixed-price/src/contract.rs b/contracts/cw721-fixed-price/src/contract.rs index e0d65711e..4ae0c4b18 100644 --- a/contracts/cw721-fixed-price/src/contract.rs +++ b/contracts/cw721-fixed-price/src/contract.rs @@ -6,16 +6,14 @@ use crate::state::{Config, CONFIG}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, ReplyOn, Response, - StdResult, SubMsg, Uint128, WasmMsg, + to_json_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Reply, + ReplyOn, Response, StdResult, SubMsg, Uint128, WasmMsg, }; use cw2::set_contract_version; use cw20::Cw20ReceiveMsg; -use cw721_base::EmptyCollectionInfoExtension; -use cw721_base::{ - helpers::Cw721Contract, msg::ExecuteMsg as Cw721ExecuteMsg, - msg::InstantiateMsg as Cw721InstantiateMsg, -}; +use cw721::helpers::Cw721Contract; +use cw721::msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}; +use cw721::state::DefaultOptionCollectionInfoExtension; use cw_utils::parse_reply_instantiate_data; // version info for migration info @@ -29,7 +27,7 @@ pub fn instantiate( deps: DepsMut, _env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -163,7 +161,7 @@ pub fn execute_receive( return Err(ContractError::WrongPaymentAmount {}); } - let mint_msg = Cw721ExecuteMsg::<_, Empty>::Mint { + let mint_msg = Cw721ExecuteMsg::<_, Empty, Empty>::Mint { token_id: config.unused_token_id.to_string(), owner: sender, token_uri: config.token_uri.clone().into(), @@ -189,7 +187,7 @@ mod tests { use super::*; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{from_json, to_json_binary, CosmosMsg, SubMsgResponse, SubMsgResult}; - use cw721_base::EmptyExtension; + use cw721::state::DefaultOptionMetadataExtension; use prost::Message; const NFT_CONTRACT_ADDR: &str = "nftcontract"; @@ -388,7 +386,7 @@ mod tests { let info = mock_info(MOCK_CONTRACT_ADDR, &[]); let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - let mint_msg = Cw721ExecuteMsg::::Mint { + let mint_msg = Cw721ExecuteMsg::::Mint { token_id: String::from("0"), owner: String::from("minter"), token_uri: Some(String::from("https://ipfs.io/ipfs/Q")), diff --git a/contracts/cw721-fixed-price/src/msg.rs b/contracts/cw721-fixed-price/src/msg.rs index a180b0e86..a27fb23b5 100644 --- a/contracts/cw721-fixed-price/src/msg.rs +++ b/contracts/cw721-fixed-price/src/msg.rs @@ -1,7 +1,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, Uint128}; use cw20::Cw20ReceiveMsg; -use cw721_base::EmptyExtension; +use cw721::state::DefaultOptionMetadataExtension; #[cw_serde] pub struct InstantiateMsg { @@ -13,7 +13,7 @@ pub struct InstantiateMsg { pub token_code_id: u64, pub cw20_address: Addr, pub token_uri: String, - pub extension: EmptyExtension, + pub extension: DefaultOptionMetadataExtension, pub collection_info_extension: TCollectionInfoExtension, pub withdraw_address: Option, } @@ -40,6 +40,6 @@ pub struct ConfigResponse { pub name: String, pub symbol: String, pub token_uri: String, - pub extension: EmptyExtension, + pub extension: DefaultOptionMetadataExtension, pub unused_token_id: u32, } diff --git a/contracts/cw721-fixed-price/src/state.rs b/contracts/cw721-fixed-price/src/state.rs index 3cfa107c8..de45b856e 100644 --- a/contracts/cw721-fixed-price/src/state.rs +++ b/contracts/cw721-fixed-price/src/state.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Uint128}; -use cw721_base::EmptyExtension; +use cw721::state::DefaultOptionMetadataExtension; use cw_storage_plus::Item; #[cw_serde] @@ -13,7 +13,7 @@ pub struct Config { pub name: String, pub symbol: String, pub token_uri: String, - pub extension: EmptyExtension, + pub extension: DefaultOptionMetadataExtension, pub unused_token_id: u32, } diff --git a/contracts/cw721-metadata-onchain/.cargo/config b/contracts/cw721-metadata-onchain/.cargo/config deleted file mode 100644 index 7d1a066c8..000000000 --- a/contracts/cw721-metadata-onchain/.cargo/config +++ /dev/null @@ -1,5 +0,0 @@ -[alias] -wasm = "build --release --target wasm32-unknown-unknown" -wasm-debug = "build --target wasm32-unknown-unknown" -unit-test = "test --lib" -schema = "run --example schema" diff --git a/contracts/cw721-metadata-onchain/Cargo.toml b/contracts/cw721-metadata-onchain/Cargo.toml deleted file mode 100644 index 75944e575..000000000 --- a/contracts/cw721-metadata-onchain/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "cw721-metadata-onchain" -description = "Example extending CW721 NFT to store metadata on chain" -authors = [ - "Ethan Frey ", - "Orkun Külçe ", -] -version = { workspace = true } -edition = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -homepage = { workspace = true } -documentation = { workspace = true } - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -# for more explicit tests, cargo test --features=backtraces -backtraces = ["cosmwasm-std/backtraces"] -# use library feature to disable all instantiate/execute/query exports -library = [] - -[dependencies] -cosmwasm-schema = { workspace = true } -cosmwasm-std = { workspace = true } -cw2 = { workspace = true } -cw721 = { workspace = true } -cw721-base = { workspace = true, features = ["library"] } -schemars = { workspace = true } -serde = { workspace = true } diff --git a/contracts/cw721-metadata-onchain/NOTICE b/contracts/cw721-metadata-onchain/NOTICE deleted file mode 100644 index bd298d741..000000000 --- a/contracts/cw721-metadata-onchain/NOTICE +++ /dev/null @@ -1,14 +0,0 @@ -Cw721_metadata_onchain -Copyright (C) 2021 Confio OÜ - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/contracts/cw721-metadata-onchain/README.md b/contracts/cw721-metadata-onchain/README.md deleted file mode 100644 index dd449656e..000000000 --- a/contracts/cw721-metadata-onchain/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# CW721 Metadata Onchain - -NFT creators may want to store their NFT metadata on-chain so other contracts are able to interact with it. -With CW721-Base in CosmWasm, we allow you to store any data on chain you wish, using a generic `extension: T`. - -In order to support on-chain metadata, and to demonstrate how to use the extension ability, we have created this simple contract. -There is no business logic here, but looking at `lib.rs` will show you how do define custom data that is included when minting and -available in all queries. - -In particular, here we define: - -```rust -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -pub struct Trait { - pub display_type: Option, - pub trait_type: String, - pub value: String, -} - -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -pub struct Metadata { - pub image: Option, - pub image_data: Option, - pub external_url: Option, - pub description: Option, - pub name: Option, - pub attributes: Option>, - pub background_color: Option, - pub animation_url: Option, - pub youtube_url: Option, -} - -pub type Extension = Option; -``` - -In particular, the fields defined conform to the properties supported in the [OpenSea Metadata Standard](https://docs.opensea.io/docs/metadata-standards). - - -This means when you query `NftInfo{name: "Enterprise"}`, you will get something like: - -```json -{ - "name": "Enterprise", - "token_uri": "https://starships.example.com/Starship/Enterprise.json", - "extension": { - "image": null, - "image_data": null, - "external_url": null, - "description": "Spaceship with Warp Drive", - "name": "Starship USS Enterprise", - "attributes": null, - "background_color": null, - "animation_url": null, - "youtube_url": null - } -} -``` - -Please look at the test code for an example usage in Rust. - -## Notice - -Feel free to use this contract out of the box, or as inspiration for further customization of cw721-base. -We will not be adding new features or business logic here. diff --git a/contracts/cw721-metadata-onchain/examples/schema.rs b/contracts/cw721-metadata-onchain/examples/schema.rs deleted file mode 100644 index 3f1d0d140..000000000 --- a/contracts/cw721-metadata-onchain/examples/schema.rs +++ /dev/null @@ -1,12 +0,0 @@ -use cosmwasm_schema::write_api; - -use cw721::EmptyCollectionInfoExtension; -use cw721_metadata_onchain::{ExecuteMsg, InstantiateMsg, QueryMsg}; - -fn main() { - write_api! { - instantiate: InstantiateMsg, - execute: ExecuteMsg, - query: QueryMsg, - } -} diff --git a/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json b/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json deleted file mode 100644 index c2b6067fa..000000000 --- a/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json +++ /dev/null @@ -1,1982 +0,0 @@ -{ - "contract_name": "cw721-metadata-onchain", - "contract_version": "0.19.0", - "idl_version": "1.0.0", - "instantiate": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "InstantiateMsg", - "type": "object", - "required": [ - "name", - "symbol" - ], - "properties": { - "collection_info_extension": { - "anyOf": [ - { - "$ref": "#/definitions/Empty" - }, - { - "type": "null" - } - ] - }, - "creator": { - "description": "The creator is the only who can update collection info.", - "type": [ - "string", - "null" - ] - }, - "minter": { - "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", - "type": [ - "string", - "null" - ] - }, - "name": { - "description": "Name of the NFT contract", - "type": "string" - }, - "symbol": { - "description": "Symbol of the NFT contract", - "type": "string" - }, - "withdraw_address": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - } - } - }, - "execute": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ExecuteMsg", - "description": "This is like Cw721ExecuteMsg but we add a Mint command for an owner to make this stand-alone. You will likely want to remove mint and use other control logic in any contract that inherits this.", - "oneOf": [ - { - "description": "Transfer is a base message to move a token to another account without triggering actions", - "type": "object", - "required": [ - "transfer_nft" - ], - "properties": { - "transfer_nft": { - "type": "object", - "required": [ - "recipient", - "token_id" - ], - "properties": { - "recipient": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Send is a base message to transfer a token to a contract and trigger an action on the receiving contract.", - "type": "object", - "required": [ - "send_nft" - ], - "properties": { - "send_nft": { - "type": "object", - "required": [ - "contract", - "msg", - "token_id" - ], - "properties": { - "contract": { - "type": "string" - }, - "msg": { - "$ref": "#/definitions/Binary" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Allows operator to transfer / send the token from the owner's account. If expiration is set, then this allowance has a time/height limit", - "type": "object", - "required": [ - "approve" - ], - "properties": { - "approve": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "expires": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Remove previously granted Approval", - "type": "object", - "required": [ - "revoke" - ], - "properties": { - "revoke": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Allows operator to transfer / send any token from the owner's account. If expiration is set, then this allowance has a time/height limit", - "type": "object", - "required": [ - "approve_all" - ], - "properties": { - "approve_all": { - "type": "object", - "required": [ - "operator" - ], - "properties": { - "expires": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "operator": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Remove previously granted ApproveAll permission", - "type": "object", - "required": [ - "revoke_all" - ], - "properties": { - "revoke_all": { - "type": "object", - "required": [ - "operator" - ], - "properties": { - "operator": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Mint a new NFT, can only be called by the contract minter", - "type": "object", - "required": [ - "mint" - ], - "properties": { - "mint": { - "type": "object", - "required": [ - "owner", - "token_id" - ], - "properties": { - "extension": { - "description": "Any custom extension used by this contract", - "anyOf": [ - { - "$ref": "#/definitions/Metadata" - }, - { - "type": "null" - } - ] - }, - "owner": { - "description": "The owner of the newly minter NFT", - "type": "string" - }, - "token_id": { - "description": "Unique ID of the NFT", - "type": "string" - }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Burn an NFT the sender has access to", - "type": "object", - "required": [ - "burn" - ], - "properties": { - "burn": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Extension msg", - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "type": "object", - "required": [ - "msg" - ], - "properties": { - "msg": { - "$ref": "#/definitions/Empty" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Sets address to send withdrawn fees to. Only owner can call this.", - "type": "object", - "required": [ - "set_withdraw_address" - ], - "properties": { - "set_withdraw_address": { - "type": "object", - "required": [ - "address" - ], - "properties": { - "address": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Removes the withdraw address, so fees are sent to the contract. Only owner can call this.", - "type": "object", - "required": [ - "remove_withdraw_address" - ], - "properties": { - "remove_withdraw_address": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Withdraw from the contract to the given address. Anyone can call this, which is okay since withdraw address has been set by owner.", - "type": "object", - "required": [ - "withdraw_funds" - ], - "properties": { - "withdraw_funds": { - "type": "object", - "required": [ - "amount" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Coin" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Update the contract's ownership. The `action` to be provided can be either to propose transferring ownership to an account, accept a pending ownership transfer, or renounce the ownership permanently.", - "type": "object", - "required": [ - "update_ownership" - ], - "properties": { - "update_ownership": { - "$ref": "#/definitions/Action" - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Action": { - "description": "Actions that can be taken to alter the contract's ownership", - "oneOf": [ - { - "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", - "type": "object", - "required": [ - "transfer_ownership" - ], - "properties": { - "transfer_ownership": { - "type": "object", - "required": [ - "new_owner" - ], - "properties": { - "expiry": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "new_owner": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", - "type": "string", - "enum": [ - "accept_ownership" - ] - }, - { - "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", - "type": "string", - "enum": [ - "renounce_ownership" - ] - } - ] - }, - "Binary": { - "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", - "type": "string" - }, - "Coin": { - "type": "object", - "required": [ - "amount", - "denom" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "denom": { - "type": "string" - } - } - }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Metadata": { - "type": "object", - "properties": { - "animation_url": { - "type": [ - "string", - "null" - ] - }, - "attributes": { - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Trait" - } - }, - "background_color": { - "type": [ - "string", - "null" - ] - }, - "description": { - "type": [ - "string", - "null" - ] - }, - "external_url": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": [ - "string", - "null" - ] - }, - "image_data": { - "type": [ - "string", - "null" - ] - }, - "name": { - "type": [ - "string", - "null" - ] - }, - "youtube_url": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Trait": { - "type": "object", - "required": [ - "trait_type", - "value" - ], - "properties": { - "display_type": { - "type": [ - "string", - "null" - ] - }, - "trait_type": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "additionalProperties": false - }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "query": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", - "oneOf": [ - { - "description": "Return the owner of the given token, error if token does not exist", - "type": "object", - "required": [ - "owner_of" - ], - "properties": { - "owner_of": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return operator that can access all of the owner's tokens.", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return approvals that a token has", - "type": "object", - "required": [ - "approvals" - ], - "properties": { - "approvals": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return approval of a given operator for all tokens of an owner, error if not set", - "type": "object", - "required": [ - "operator" - ], - "properties": { - "operator": { - "type": "object", - "required": [ - "operator", - "owner" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "operator": { - "type": "string" - }, - "owner": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "List all operators that can access all of the owner's tokens", - "type": "object", - "required": [ - "all_operators" - ], - "properties": { - "all_operators": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired items, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Total number of tokens issued", - "type": "object", - "required": [ - "num_tokens" - ], - "properties": { - "num_tokens": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "deprecated": true, - "type": "object", - "required": [ - "contract_info" - ], - "properties": { - "contract_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns top-level metadata about the contract", - "type": "object", - "required": [ - "collection_info" - ], - "properties": { - "collection_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", - "type": "object", - "required": [ - "nft_info" - ], - "properties": { - "nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization for clients", - "type": "object", - "required": [ - "all_nft_info" - ], - "properties": { - "all_nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With Enumerable extension. Requires pagination. Lists all token_ids controlled by the contract.", - "type": "object", - "required": [ - "all_tokens" - ], - "properties": { - "all_tokens": { - "type": "object", - "properties": { - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return the minter", - "type": "object", - "required": [ - "minter" - ], - "properties": { - "minter": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Extension query", - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "type": "object", - "required": [ - "msg" - ], - "properties": { - "msg": { - "$ref": "#/definitions/Empty" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_withdraw_address" - ], - "properties": { - "get_withdraw_address": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Query the contract's ownership information", - "type": "object", - "required": [ - "ownership" - ], - "properties": { - "ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - } - } - }, - "migrate": null, - "sudo": null, - "responses": { - "all_nft_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AllNftInfoResponse_for_Empty", - "type": "object", - "required": [ - "access", - "info" - ], - "properties": { - "access": { - "description": "Who can transfer the token", - "allOf": [ - { - "$ref": "#/definitions/OwnerOfResponse" - } - ] - }, - "info": { - "description": "Data on the token itself,", - "allOf": [ - { - "$ref": "#/definitions/NftInfoResponse_for_Empty" - } - ] - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "NftInfoResponse_for_Empty": { - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "description": "You can add any custom metadata here when you extend cw721-base", - "allOf": [ - { - "$ref": "#/definitions/Empty" - } - ] - }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "OwnerOfResponse": { - "type": "object", - "required": [ - "approvals", - "owner" - ], - "properties": { - "approvals": { - "description": "If set this address is approved to transfer/send the token as well", - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - }, - "owner": { - "description": "Owner of the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "all_operators": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OperatorsResponse", - "type": "object", - "required": [ - "operators" - ], - "properties": { - "operators": { - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "all_tokens": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "TokensResponse", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "description": "Contains all token_ids in lexicographical ordering If there are more than `limit`, use `start_after` in future queries to achieve pagination.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - }, - "approval": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ApprovalResponse", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "$ref": "#/definitions/Approval" - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "approvals": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ApprovalsResponse", - "type": "object", - "required": [ - "approvals" - ], - "properties": { - "approvals": { - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "collection_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Empty", - "type": "object", - "required": [ - "extension", - "name", - "symbol", - "updated_at" - ], - "properties": { - "extension": { - "$ref": "#/definitions/Empty" - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "contract_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Empty", - "type": "object", - "required": [ - "extension", - "name", - "symbol", - "updated_at" - ], - "properties": { - "extension": { - "$ref": "#/definitions/Empty" - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "extension": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Null", - "type": "null" - }, - "get_withdraw_address": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Nullable_String", - "type": [ - "string", - "null" - ] - }, - "minter": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "MinterResponse", - "description": "Shows who can mint these tokens", - "type": "object", - "properties": { - "minter": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "nft_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NftInfoResponse_for_Empty", - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "description": "You can add any custom metadata here when you extend cw721-base", - "allOf": [ - { - "$ref": "#/definitions/Empty" - } - ] - }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - } - } - }, - "num_tokens": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NumTokensResponse", - "type": "object", - "required": [ - "count" - ], - "properties": { - "count": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "operator": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OperatorResponse", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "$ref": "#/definitions/Approval" - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "owner_of": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OwnerOfResponse", - "type": "object", - "required": [ - "approvals", - "owner" - ], - "properties": { - "approvals": { - "description": "If set this address is approved to transfer/send the token as well", - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - }, - "owner": { - "description": "Owner of the token", - "type": "string" - } - }, - "additionalProperties": false, - "definitions": { - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "ownership": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Ownership_for_String", - "description": "The contract's ownership info", - "type": "object", - "properties": { - "owner": { - "description": "The contract's current owner. `None` if the ownership has been renounced.", - "type": [ - "string", - "null" - ] - }, - "pending_expiry": { - "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "pending_owner": { - "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false, - "definitions": { - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "tokens": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "TokensResponse", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "description": "Contains all token_ids in lexicographical ordering If there are more than `limit`, use `start_after` in future queries to achieve pagination.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - } - } -} diff --git a/contracts/cw721-metadata-onchain/src/lib.rs b/contracts/cw721-metadata-onchain/src/lib.rs deleted file mode 100644 index 27056c676..000000000 --- a/contracts/cw721-metadata-onchain/src/lib.rs +++ /dev/null @@ -1,130 +0,0 @@ -use cosmwasm_std::Empty; -use cw721::MetadataExtension; -pub use cw721_base::{ContractError, EmptyCollectionInfoExtension, InstantiateMsg, MinterResponse}; - -// Version info for migration -const CONTRACT_NAME: &str = "crates.io:cw721-metadata-onchain"; -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); - -pub type Cw721MetadataContract<'a> = cw721_base::Cw721Contract< - 'a, - MetadataExtension, - Empty, - Empty, - Empty, - EmptyCollectionInfoExtension, ->; -pub type ExecuteMsg = cw721_base::ExecuteMsg; -pub type QueryMsg = cw721_base::QueryMsg; - -#[cfg(not(feature = "library"))] -pub mod entry { - use super::*; - - use cosmwasm_std::entry_point; - use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; - - // This makes a conscious choice on the various generics used by the contract - #[entry_point] - pub fn instantiate( - mut deps: DepsMut, - env: Env, - info: MessageInfo, - msg: InstantiateMsg, - ) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - Cw721MetadataContract::default().instantiate(deps.branch(), env, info, msg) - } - - #[entry_point] - pub fn execute( - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, - ) -> Result { - Cw721MetadataContract::default().execute(deps, env, info, msg) - } - - #[entry_point] - pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { - Cw721MetadataContract::default().query(deps, env, msg) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cw721::{Cw721Query, Metadata}; - - const CREATOR: &str = "creator"; - - /// Make sure cw2 version info is properly initialized during instantiation, - /// and NOT overwritten by the base contract. - #[test] - fn proper_cw2_initialization() { - let mut deps = mock_dependencies(); - - entry::instantiate( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - InstantiateMsg { - name: "collection_name".into(), - symbol: "collection_symbol".into(), - collection_info_extension: None, - minter: Some("minter".into()), - creator: Some("creator".into()), - withdraw_address: None, - }, - ) - .unwrap(); - - let version = cw2::get_contract_version(deps.as_ref().storage).unwrap(); - assert_eq!(version.contract, CONTRACT_NAME); - assert_ne!(version.contract, cw721_base::CONTRACT_NAME); - } - - #[test] - fn use_metadata_extension() { - let mut deps = mock_dependencies(); - let contract = Cw721MetadataContract::default(); - - let info = mock_info(CREATOR, &[]); - let init_msg = InstantiateMsg { - name: "collection_name".into(), - symbol: "collection_symbol".into(), - collection_info_extension: None, - minter: None, - creator: None, - withdraw_address: None, - }; - contract - .instantiate(deps.as_mut(), mock_env(), info.clone(), init_msg) - .unwrap(); - - let token_id = "Enterprise"; - let token_uri = Some("https://starships.example.com/Starship/Enterprise.json".into()); - let extension = Some(Metadata { - description: Some("Spaceship with Warp Drive".into()), - name: Some("Starship USS Enterprise".to_string()), - ..Metadata::default() - }); - let exec_msg = ExecuteMsg::Mint { - token_id: token_id.to_string(), - owner: "john".to_string(), - token_uri: token_uri.clone(), - extension: extension.clone(), - }; - contract - .execute(deps.as_mut(), mock_env(), info, exec_msg) - .unwrap(); - - let res = contract.nft_info(deps.as_ref(), token_id.into()).unwrap(); - assert_eq!(res.token_uri, token_uri); - assert_eq!(res.extension, extension); - } -} diff --git a/contracts/cw721-non-transferable/examples/schema.rs b/contracts/cw721-non-transferable/examples/schema.rs index 6c115d7a5..55616cda9 100644 --- a/contracts/cw721-non-transferable/examples/schema.rs +++ b/contracts/cw721-non-transferable/examples/schema.rs @@ -3,13 +3,17 @@ use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; -use cw721::EmptyCollectionInfoExtension; -#[allow(deprecated)] +use cosmwasm_std::Empty; use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721ExecuteMsg, - NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + msg::{Cw721ExecuteMsg, MinterResponse}, + query::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, + NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + }, + state::{CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, }; -use cw721_non_transferable::{EmptyExtension, InstantiateMsg, MinterResponse, QueryMsg}; +#[allow(deprecated)] +use cw721_non_transferable::{InstantiateMsg, QueryMsg}; fn main() { let mut out_dir = current_dir().unwrap(); @@ -18,14 +22,23 @@ fn main() { remove_schemas(&out_dir).unwrap(); export_schema_with_title( - &schema_for!(InstantiateMsg), + &schema_for!(InstantiateMsg), &out_dir, "InstantiateMsg", ); - export_schema_with_title(&schema_for!(Cw721ExecuteMsg), &out_dir, "Cw721ExecuteMsg"); + export_schema_with_title( + &schema_for!(Cw721ExecuteMsg), + &out_dir, + "ExecuteMsg", + ); export_schema(&schema_for!(QueryMsg), &out_dir); export_schema_with_title( - &schema_for!(AllNftInfoResponse), + &schema_for!(NftInfoResponse), + &out_dir, + "NftInfoResponse", + ); + export_schema_with_title( + &schema_for!(AllNftInfoResponse), &out_dir, "AllNftInfoResponse", ); @@ -33,16 +46,11 @@ fn main() { export_schema(&schema_for!(ApprovalsResponse), &out_dir); export_schema(&schema_for!(OperatorsResponse), &out_dir); export_schema_with_title( - &schema_for!(CollectionInfo), + &schema_for!(CollectionInfo), &out_dir, "CollectionInfo", ); export_schema(&schema_for!(MinterResponse), &out_dir); - export_schema_with_title( - &schema_for!(NftInfoResponse), - &out_dir, - "NftInfoResponse", - ); export_schema(&schema_for!(NumTokensResponse), &out_dir); export_schema(&schema_for!(OwnerOfResponse), &out_dir); export_schema(&schema_for!(TokensResponse), &out_dir); diff --git a/contracts/cw721-non-transferable/schema/all_nft_info_response.json b/contracts/cw721-non-transferable/schema/all_nft_info_response.json index d263321e2..8a03851da 100644 --- a/contracts/cw721-non-transferable/schema/all_nft_info_response.json +++ b/contracts/cw721-non-transferable/schema/all_nft_info_response.json @@ -19,13 +19,17 @@ "description": "Data on the token itself,", "allOf": [ { - "$ref": "#/definitions/NftInfoResponse_for_Nullable_Empty" + "$ref": "#/definitions/NftInfoResponse_for_Nullable_Metadata" } ] } }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -43,15 +47,15 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", "oneOf": [ @@ -99,14 +103,77 @@ } ] }, - "NftInfoResponse_for_Nullable_Empty": { + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "NftInfoResponse_for_Nullable_Metadata": { "type": "object", "properties": { "extension": { "description": "You can add any custom metadata here when you extend cw721-base", "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/Metadata" }, { "type": "null" @@ -152,6 +219,28 @@ } ] }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, "Uint64": { "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", "type": "string" diff --git a/contracts/cw721-non-transferable/schema/approval_response.json b/contracts/cw721-non-transferable/schema/approval_response.json index b29eab59e..c72b71b2d 100644 --- a/contracts/cw721-non-transferable/schema/approval_response.json +++ b/contracts/cw721-non-transferable/schema/approval_response.json @@ -12,6 +12,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -29,7 +33,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false diff --git a/contracts/cw721-non-transferable/schema/approvals_response.json b/contracts/cw721-non-transferable/schema/approvals_response.json index 7cdac0015..01caf4d31 100644 --- a/contracts/cw721-non-transferable/schema/approvals_response.json +++ b/contracts/cw721-non-transferable/schema/approvals_response.json @@ -15,6 +15,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -32,7 +36,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false diff --git a/contracts/cw721-non-transferable/schema/collection_info.json b/contracts/cw721-non-transferable/schema/collection_info.json index 431c4d037..bae78d23a 100644 --- a/contracts/cw721-non-transferable/schema/collection_info.json +++ b/contracts/cw721-non-transferable/schema/collection_info.json @@ -11,7 +11,7 @@ "extension": { "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" }, { "type": "null" @@ -30,9 +30,77 @@ }, "additionalProperties": false, "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false }, "Timestamp": { "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", diff --git a/contracts/cw721-non-transferable/schema/cw721_execute_msg.json b/contracts/cw721-non-transferable/schema/cw721_execute_msg.json deleted file mode 100644 index 228b5c249..000000000 --- a/contracts/cw721-non-transferable/schema/cw721_execute_msg.json +++ /dev/null @@ -1,265 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Cw721ExecuteMsg", - "oneOf": [ - { - "description": "Transfer is a base message to move a token to another account without triggering actions", - "type": "object", - "required": [ - "transfer_nft" - ], - "properties": { - "transfer_nft": { - "type": "object", - "required": [ - "recipient", - "token_id" - ], - "properties": { - "recipient": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Send is a base message to transfer a token to a contract and trigger an action on the receiving contract.", - "type": "object", - "required": [ - "send_nft" - ], - "properties": { - "send_nft": { - "type": "object", - "required": [ - "contract", - "msg", - "token_id" - ], - "properties": { - "contract": { - "type": "string" - }, - "msg": { - "$ref": "#/definitions/Binary" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Allows operator to transfer / send the token from the owner's account. If expiration is set, then this allowance has a time/height limit", - "type": "object", - "required": [ - "approve" - ], - "properties": { - "approve": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "expires": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Remove previously granted Approval", - "type": "object", - "required": [ - "revoke" - ], - "properties": { - "revoke": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Allows operator to transfer / send any token from the owner's account. If expiration is set, then this allowance has a time/height limit", - "type": "object", - "required": [ - "approve_all" - ], - "properties": { - "approve_all": { - "type": "object", - "required": [ - "operator" - ], - "properties": { - "expires": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "operator": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Remove previously granted ApproveAll permission", - "type": "object", - "required": [ - "revoke_all" - ], - "properties": { - "revoke_all": { - "type": "object", - "required": [ - "operator" - ], - "properties": { - "operator": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Burn an NFT the sender has access to", - "type": "object", - "required": [ - "burn" - ], - "properties": { - "burn": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Binary": { - "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", - "type": "string" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } -} diff --git a/contracts/cw721-non-transferable/schema/execute_msg.json b/contracts/cw721-non-transferable/schema/execute_msg.json new file mode 100644 index 000000000..549baff69 --- /dev/null +++ b/contracts/cw721-non-transferable/schema/execute_msg.json @@ -0,0 +1,707 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "deprecated": true, + "type": "object", + "required": [ + "update_ownership" + ], + "properties": { + "update_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_minter_ownership" + ], + "properties": { + "update_minter_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_creator_ownership" + ], + "properties": { + "update_creator_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "description": "Update the collection, can only called by the contract creator", + "type": "object", + "required": [ + "update_collection_info" + ], + "properties": { + "update_collection_info": { + "type": "object", + "required": [ + "collection_info" + ], + "properties": { + "collection_info": { + "$ref": "#/definitions/CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Transfer is a base message to move a token to another account without triggering actions", + "type": "object", + "required": [ + "transfer_nft" + ], + "properties": { + "transfer_nft": { + "type": "object", + "required": [ + "recipient", + "token_id" + ], + "properties": { + "recipient": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Send is a base message to transfer a token to a contract and trigger an action on the receiving contract.", + "type": "object", + "required": [ + "send_nft" + ], + "properties": { + "send_nft": { + "type": "object", + "required": [ + "contract", + "msg", + "token_id" + ], + "properties": { + "contract": { + "type": "string" + }, + "msg": { + "$ref": "#/definitions/Binary" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Allows operator to transfer / send the token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "type": "object", + "required": [ + "approve" + ], + "properties": { + "approve": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Remove previously granted Approval", + "type": "object", + "required": [ + "revoke" + ], + "properties": { + "revoke": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Allows operator to transfer / send any token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "type": "object", + "required": [ + "approve_all" + ], + "properties": { + "approve_all": { + "type": "object", + "required": [ + "operator" + ], + "properties": { + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "operator": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Remove previously granted ApproveAll permission", + "type": "object", + "required": [ + "revoke_all" + ], + "properties": { + "revoke_all": { + "type": "object", + "required": [ + "operator" + ], + "properties": { + "operator": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Mint a new NFT, can only be called by the contract minter", + "type": "object", + "required": [ + "mint" + ], + "properties": { + "mint": { + "type": "object", + "required": [ + "owner", + "token_id" + ], + "properties": { + "extension": { + "description": "Any custom extension used by this contract", + "anyOf": [ + { + "$ref": "#/definitions/Metadata" + }, + { + "type": "null" + } + ] + }, + "owner": { + "description": "The owner of the newly minter NFT", + "type": "string" + }, + "token_id": { + "description": "Unique ID of the NFT", + "type": "string" + }, + "token_uri": { + "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Burn an NFT the sender has access to", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Extension msg", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Sets address to send withdrawn fees to. Only owner can call this.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Removes the withdraw address, so fees are sent to the contract. Only owner can call this.", + "type": "object", + "required": [ + "remove_withdraw_address" + ], + "properties": { + "remove_withdraw_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Withdraw from the contract to the given address. Anyone can call this, which is okay since withdraw address has been set by owner.", + "type": "object", + "required": [ + "withdraw_funds" + ], + "properties": { + "withdraw_funds": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Action": { + "description": "Actions that can be taken to alter the contract's ownership", + "oneOf": [ + { + "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", + "type": "object", + "required": [ + "transfer_ownership" + ], + "properties": { + "transfer_ownership": { + "type": "object", + "required": [ + "new_owner" + ], + "properties": { + "expiry": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "new_owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", + "type": "string", + "enum": [ + "accept_ownership" + ] + }, + { + "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", + "type": "string", + "enum": [ + "renounce_ownership" + ] + } + ] + }, + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "name", + "symbol" + ], + "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/cw721-non-transferable/schema/instantiate_msg.json b/contracts/cw721-non-transferable/schema/instantiate_msg.json index db962cae7..cbffb9324 100644 --- a/contracts/cw721-non-transferable/schema/instantiate_msg.json +++ b/contracts/cw721-non-transferable/schema/instantiate_msg.json @@ -16,7 +16,7 @@ "collection_info_extension": { "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" }, { "type": "null" @@ -50,9 +50,89 @@ }, "additionalProperties": false, "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" } } } diff --git a/contracts/cw721-non-transferable/schema/nft_info_response.json b/contracts/cw721-non-transferable/schema/nft_info_response.json index 51b1f072c..0f0859230 100644 --- a/contracts/cw721-non-transferable/schema/nft_info_response.json +++ b/contracts/cw721-non-transferable/schema/nft_info_response.json @@ -7,7 +7,7 @@ "description": "You can add any custom metadata here when you extend cw721-base", "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/Metadata" }, { "type": "null" @@ -24,9 +24,90 @@ }, "additionalProperties": false, "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false } } } diff --git a/contracts/cw721-non-transferable/schema/operators_response.json b/contracts/cw721-non-transferable/schema/operators_response.json index 533a096dd..426cd48cc 100644 --- a/contracts/cw721-non-transferable/schema/operators_response.json +++ b/contracts/cw721-non-transferable/schema/operators_response.json @@ -15,6 +15,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -32,7 +36,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false diff --git a/contracts/cw721-non-transferable/schema/owner_of_response.json b/contracts/cw721-non-transferable/schema/owner_of_response.json index abb9006d8..36b85a171 100644 --- a/contracts/cw721-non-transferable/schema/owner_of_response.json +++ b/contracts/cw721-non-transferable/schema/owner_of_response.json @@ -21,6 +21,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -38,7 +42,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false diff --git a/contracts/cw721-non-transferable/schema/query_msg.json b/contracts/cw721-non-transferable/schema/query_msg.json index 33853b647..4c2d624d3 100644 --- a/contracts/cw721-non-transferable/schema/query_msg.json +++ b/contracts/cw721-non-transferable/schema/query_msg.json @@ -171,10 +171,36 @@ { "type": "object", "required": [ - "collection_info" + "get_collection_info" ], "properties": { - "collection_info": { + "get_collection_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_minter_ownership" + ], + "properties": { + "get_minter_ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_creator_ownership" + ], + "properties": { + "get_creator_ownership": { "type": "object", "additionalProperties": false } @@ -294,6 +320,7 @@ "additionalProperties": false }, { + "deprecated": true, "type": "object", "required": [ "minter" @@ -305,6 +332,19 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_withdraw_address" + ], + "properties": { + "get_withdraw_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false } ] } diff --git a/contracts/cw721-non-transferable/src/lib.rs b/contracts/cw721-non-transferable/src/lib.rs index c56956424..a4d672ce1 100644 --- a/contracts/cw721-non-transferable/src/lib.rs +++ b/contracts/cw721-non-transferable/src/lib.rs @@ -1,9 +1,9 @@ pub use crate::msg::{InstantiateMsg, QueryMsg}; use cosmwasm_std::Empty; +use cw721::state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}; pub use cw721_base::{ entry::{execute as _execute, query as _query}, - ContractError, Cw721Contract, EmptyCollectionInfoExtension, EmptyExtension, ExecuteMsg, - InstantiateMsg as Cw721BaseInstantiateMsg, MinterResponse, + Cw721Contract, }; pub mod msg; @@ -14,8 +14,14 @@ pub mod state; const CONTRACT_NAME: &str = "crates.io:cw721-non-transferable"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -pub type Cw721NonTransferableContract<'a> = - Cw721Contract<'a, EmptyExtension, Empty, Empty, Empty, EmptyCollectionInfoExtension>; +pub type Cw721NonTransferableContract<'a> = Cw721Contract< + 'a, + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, +>; #[cfg(not(feature = "library"))] pub mod entry { @@ -26,14 +32,17 @@ pub mod entry { entry_point, to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, }; + use cw721::error::Cw721ContractError; + use cw721::execute::Cw721Execute; + use cw721::msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}; #[entry_point] pub fn instantiate( mut deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, - ) -> Result { + msg: InstantiateMsg, + ) -> Result { let admin_addr: Option = msg .admin .as_deref() @@ -44,7 +53,7 @@ pub mod entry { CONFIG.save(deps.storage, &config)?; - let cw721_base_instantiate_msg = Cw721BaseInstantiateMsg { + let cw721_base_instantiate_msg = Cw721InstantiateMsg { name: msg.name, symbol: msg.symbol, collection_info_extension: msg.collection_info_extension, @@ -58,6 +67,8 @@ pub mod entry { env, info, cw721_base_instantiate_msg, + "contract_name", + "contract_version", )?; cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -72,28 +83,32 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: ExecuteMsg, - ) -> Result { + msg: Cw721ExecuteMsg< + DefaultOptionMetadataExtension, + Empty, + DefaultOptionCollectionInfoExtension, + >, + ) -> Result { let config = CONFIG.load(deps.storage)?; match config.admin { Some(admin) => { if admin == info.sender { _execute(deps, env, info, msg) } else { - Err(ContractError::Ownership( + Err(Cw721ContractError::Ownership( cw721_base::OwnershipError::NotOwner, )) } } None => match msg { - ExecuteMsg::Mint { + Cw721ExecuteMsg::Mint { token_id, owner, token_uri, extension, } => Cw721NonTransferableContract::default() .mint(deps, info, token_id, owner, token_uri, extension), - _ => Err(ContractError::Ownership( + _ => Err(Cw721ContractError::Ownership( cw721_base::OwnershipError::NotOwner, )), }, diff --git a/contracts/cw721-non-transferable/src/msg.rs b/contracts/cw721-non-transferable/src/msg.rs index e917c2991..c27b48746 100644 --- a/contracts/cw721-non-transferable/src/msg.rs +++ b/contracts/cw721-non-transferable/src/msg.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::Empty; -use cw721_base::msg::QueryMsg as Cw721QueryMsg; +use cw721::{msg::Cw721QueryMsg, state::DefaultOptionCollectionInfoExtension}; #[cw_serde] pub struct InstantiateMsg { @@ -64,8 +64,8 @@ pub enum QueryMsg { GetWithdrawAddress {}, } -impl From for Cw721QueryMsg { - fn from(msg: QueryMsg) -> Cw721QueryMsg { +impl From for Cw721QueryMsg { + fn from(msg: QueryMsg) -> Cw721QueryMsg { match msg { QueryMsg::OwnerOf { token_id, diff --git a/contracts/cw721-receiver-tester/src/msg.rs b/contracts/cw721-receiver-tester/src/msg.rs index 137c09046..f6b7cce50 100644 --- a/contracts/cw721-receiver-tester/src/msg.rs +++ b/contracts/cw721-receiver-tester/src/msg.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cw721::Cw721ReceiveMsg; +use cw721::receiver::Cw721ReceiveMsg; #[cw_serde] pub struct InstantiateMsg {} diff --git a/contracts/cw721-receiver-tester/tests/multitest.rs b/contracts/cw721-receiver-tester/tests/multitest.rs index ff2e535c4..be1769fdb 100644 --- a/contracts/cw721-receiver-tester/tests/multitest.rs +++ b/contracts/cw721-receiver-tester/tests/multitest.rs @@ -1,4 +1,5 @@ use cosmwasm_std::{to_json_binary, Addr, Attribute, Binary, Empty}; +use cw721::state::DefaultOptionCollectionInfoExtension; use cw_multi_test::{App, ContractWrapper, Executor}; #[test] @@ -19,7 +20,7 @@ fn test_cw721_base_receive_succeed() { .execute_contract( admin.clone(), nft_contract, - &ExecuteMsg::<(), ()>::SendNft { + &ExecuteMsg::<(), (), ()>::SendNft { contract: receiver_contract.to_string(), token_id: "test".to_string(), msg: to_json_binary(&InnerMsg::Succeed).unwrap(), @@ -75,7 +76,7 @@ fn test_cw721_base_receive_fail() { let result = app.execute_contract( admin.clone(), nft_contract.clone(), - &ExecuteMsg::<(), ()>::SendNft { + &ExecuteMsg::<(), (), ()>::SendNft { contract: receiver_contract.to_string(), token_id: "test".to_string(), msg: to_json_binary(&InnerMsg::Fail).unwrap(), @@ -88,7 +89,7 @@ fn test_cw721_base_receive_fail() { let result = app.execute_contract( admin, nft_contract, - &ExecuteMsg::<(), ()>::SendNft { + &ExecuteMsg::<(), (), ()>::SendNft { contract: receiver_contract.to_string(), token_id: "test".to_string(), msg: Binary::from(br#"{"invalid": "fields"}"#), @@ -122,10 +123,10 @@ fn setup_contracts(app: &mut App, admin: Addr) -> Contracts { .instantiate_contract( nft_code_id, admin.clone(), - &base_msg::InstantiateMsg { + &base_msg::InstantiateMsg:: { name: "nft".to_string(), symbol: "NFT".to_string(), - collection_info_extension: Empty {}, + collection_info_extension: None, minter: Some(admin.to_string()), creator: Some(admin.to_string()), withdraw_address: None, @@ -151,7 +152,7 @@ fn setup_contracts(app: &mut App, admin: Addr) -> Contracts { app.execute_contract( admin.clone(), nft_contract.clone(), - &base_msg::ExecuteMsg::<(), ()>::Mint { + &base_msg::ExecuteMsg::<(), (), ()>::Mint { token_id: "test".to_string(), owner: admin.to_string(), token_uri: Some("https://example.com".to_string()), diff --git a/packages/cw721/Cargo.toml b/packages/cw721/Cargo.toml index 0f5967cff..e6f4775cc 100644 --- a/packages/cw721/Cargo.toml +++ b/packages/cw721/Cargo.toml @@ -15,6 +15,17 @@ documentation = { workspace = true } [dependencies] cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } +cw-ownable = { workspace = true } +cw-storage-plus = { workspace = true } cw-utils = { workspace = true } +cw2 = { workspace = true } +cw721-016 = { workspace = true } schemars = { workspace = true } serde = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +cw-multi-test = { workspace = true } +cw721-base-016 = { workspace = true, features = ["library"] } +cw721-base-017 = { workspace = true, features = ["library"] } +cw721-base-018 = { workspace = true, features = ["library"] } diff --git a/packages/cw721/README.md b/packages/cw721/README.md index 883809f04..b9e0ee991 100644 --- a/packages/cw721/README.md +++ b/packages/cw721/README.md @@ -1,42 +1,58 @@ # CW721 Spec: Non Fungible Tokens -CW721 is a specification for non-fungible tokens based on CosmWasm. -The name and design is based on Ethereum's ERC721 standard, -with some enhancements. The types in here can be imported by -contracts that wish to implement this spec, or by contracts that call +CW721 is a specification for non-fungible tokens (NFTs) based on CosmWasm. +The name and design is based on Ethereum's [ERC721](https://eips.ethereum.org/EIPS/eip-721) standard, +with some enhancements. The types in here can be imported by +contracts that wish to implement this spec, or by contracts that call to any standard cw721 contract. The specification is split into multiple sections, a contract may only implement some of this functionality, but must implement the base. -## Base +## `cw721` package + +The CW721 package provides 2 traits with default implementations (aka `utilities`): + +- `Cw721Execute` with `Cw721ExecuteMsg` e.g. for minting, burning, + sending, approving, and transferring NFTs. It also allows updating + collection info, withdraw address, creator and minter ownership. +- `Cw721Query` with `Cw721QueryMsg` e.g. for NFTs, tokens, approvals, various kinds of ownerships (creator and minter). + +Default implementations are opionated and uses a `Cw721Config` store. Custom cw721 +contracts may re-implement each utlitiy to their own need. + +### `cw721-base` This handles ownership, transfers, and allowances. These must be supported -as is by all CW721 contracts. Note that all tokens must have an owner, +as is by all CW721 contracts. Note that all tokens must have an owner, as well as an ID. The ID is an arbitrary string, unique within the contract. +`cw721-base` contract is the base contract for handling NFT collections with +either offchain (stored in `token_uri`) or onchain (stored in `NftInfo`'s extension') +metadata. Contract itself is lightweight, since all logic is provided in `cw721` package. + ### Messages -`TransferNft{recipient, token_id}` - -This transfers ownership of the token to `recipient` account. This is -designed to send to an address controlled by a private key and *does not* +`TransferNft{recipient, token_id}` - +This transfers ownership of the token to `recipient` account. This is +designed to send to an address controlled by a private key and _does not_ trigger any actions on the recipient if it is a contract. -Requires `token_id` to point to a valid token, and `env.sender` to be -the owner of it, or have an allowance to transfer it. +Requires `token_id` to point to a valid token, and `env.sender` to be +the owner of it, or have an allowance to transfer it. -`SendNft{contract, token_id, msg}` - -This transfers ownership of the token to `contract` account. `contract` +`SendNft{contract, token_id, msg}` - +This transfers ownership of the token to `contract` account. `contract` must be an address controlled by a smart contract, which implements -the CW721Receiver interface. The `msg` will be passed to the recipient +the CW721Receiver interface. The `msg` will be passed to the recipient contract, along with the token_id. -Requires `token_id` to point to a valid token, and `env.sender` to be -the owner of it, or have an allowance to transfer it. +Requires `token_id` to point to a valid token, and `env.sender` to be +the owner of it, or have an allowance to transfer it. `Approve{spender, token_id, expires}` - Grants permission to `spender` to transfer or send the given token. This can only be performed when -`env.sender` is the owner of the given `token_id` or an `operator`. +`env.sender` is the owner of the given `token_id` or an `operator`. There can be multiple spender accounts per token, and they are cleared once the token is transferred or sent. @@ -72,14 +88,14 @@ expired owners in the results, otherwise, ignore them. operators that can access all of the owner's tokens. Return type is `OperatorsResponse`. If `include_expired` is set, show expired owners in the results, otherwise, ignore them. If `start_after` is set, then it returns the -first `limit` operators *after* the given one. +first `limit` operators _after_ the given one. `NumTokens{}` - Total number of tokens issued ### Receiver The counter-part to `SendNft` is `ReceiveNft`, which must be implemented by -any contract that wishes to manage CW721 tokens. This is generally *not* +any contract that wishes to manage CW721 tokens. This is generally _not_ implemented by any CW721 contract. `ReceiveNft{sender, token_id, msg}` - This is designed to handle `SendNft` @@ -91,18 +107,18 @@ The `sender` is the original account requesting to move the token and `msg` is a `Binary` data that can be decoded into a contract-specific message. This can be empty if we have only one default action, or it may be a `ReceiveMsg` variant to clarify the intention. For example, -if I send to an exchange, I can specify the price I want to list the token +if I send to an exchange, I can specify the price I want to list the token for. - + ## Metadata ### Queries -`ContractInfo{}` - This returns top-level metadata about the contract. +`CollectionInfo{}` - This returns top-level metadata about the contract. Namely, `name` and `symbol`. `NftInfo{token_id}` - This returns metadata about one particular token. -The return value is based on *ERC721 Metadata JSON Schema*, but directly +The return value is based on _ERC721 Metadata JSON Schema_, but directly from the contract, not as a Uri. Only the image link is a Uri. `AllNftInfo{token_id}` - This returns the result of both `NftInfo` @@ -120,14 +136,72 @@ value (suggested 30). Contracts can define other `DefaultLimit` and `MaxLimit` values without violating the CW721 spec, and clients should not rely on any particular values. -If `start_after` is unset, the query returns the first results, ordered +If `start_after` is unset, the query returns the first results, ordered lexicographically by `token_id`. If `start_after` is set, then it returns the -first `limit` tokens *after* the given one. This allows straightforward +first `limit` tokens _after_ the given one. This allows straightforward pagination by taking the last result returned (a `token_id`) and using it -as the `start_after` value in a future query. +as the `start_after` value in a future query. `Tokens{owner, start_after, limit}` - List all token_ids that belong to a given owner. Return type is `TokensResponse{tokens: Vec}`. -`AllTokens{start_after, limit}` - Requires pagination. Lists all token_ids controlled by +`AllTokens{start_after, limit}` - Requires pagination. Lists all token_ids controlled by the contract. + +### NftInfo Extension - CW721 Metadata Onchain + +NFT creators may want to store their NFT metadata on-chain so other contracts are able to interact with it. +With CW721 in CosmWasm, we allow you to store any data on chain you wish, using a generic `extension: T`. + +In order to support on-chain metadata, and to demonstrate how to use the extension ability, we have created this simple contract. +There is no business logic here, but looking at `lib.rs` will show you how do define custom data that is included when minting and +available in all queries. + +In particular, here we define: + +```rust +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +pub struct Trait { + pub display_type: Option, + pub trait_type: String, + pub value: String, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +pub struct Metadata { + pub image: Option, + pub image_data: Option, + pub external_url: Option, + pub description: Option, + pub name: Option, + pub attributes: Option>, + pub background_color: Option, + pub animation_url: Option, + pub youtube_url: Option, +} + +pub type Extension = Option; +``` + +In particular, the fields defined conform to the properties supported in the [OpenSea Metadata Standard](https://docs.opensea.io/docs/metadata-standards). + + +This means when you query `NftInfo{name: "Enterprise"}`, you will get something like: + +```json +{ + "name": "Enterprise", + "token_uri": "https://starships.example.com/Starship/Enterprise.json", + "extension": { + "image": null, + "image_data": null, + "external_url": null, + "description": "Spaceship with Warp Drive", + "name": "Starship USS Enterprise", + "attributes": null, + "background_color": null, + "animation_url": null, + "youtube_url": null + } +} +``` diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 25028c406..2fe822c69 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -4,24 +4,42 @@ use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; use cosmwasm_std::Empty; -#[allow(deprecated)] use cw721::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, Cw721ExecuteMsg, - Cw721QueryMsg, Cw721ReceiveMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, - OperatorsResponse, OwnerOfResponse, TokensResponse, + msg::{Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721QueryMsg}, + query::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, + NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + }, + receiver::Cw721ReceiveMsg, + state::{CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, }; -use cw721::{EmptyCollectionInfoExtension, EmptyExtension}; fn main() { let mut out_dir = current_dir().unwrap(); out_dir.push("schema"); create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); - export_schema(&schema_for!(Cw721ExecuteMsg), &out_dir); - export_schema(&schema_for!(Cw721QueryMsg), &out_dir); + export_schema_with_title(&schema_for!(Cw721InstantiateMsg), &out_dir, "InstantiateMsg"); + export_schema_with_title( + &schema_for!( + Cw721ExecuteMsg::< + DefaultOptionMetadataExtension, + Empty, + DefaultOptionCollectionInfoExtension, + > + ), + &out_dir, + "ExecuteMsg" + ); + export_schema_with_title(&schema_for!(Cw721QueryMsg), &out_dir, "QueryMsg"); export_schema(&schema_for!(Cw721ReceiveMsg), &out_dir); export_schema_with_title( - &schema_for!(AllNftInfoResponse), + &schema_for!(NftInfoResponse), + &out_dir, + "NftInfoResponse", + ); + export_schema_with_title( + &schema_for!(AllNftInfoResponse), &out_dir, "AllNftInfoResponse", ); @@ -30,16 +48,11 @@ fn main() { export_schema(&schema_for!(OperatorResponse), &out_dir); export_schema(&schema_for!(OperatorsResponse), &out_dir); export_schema_with_title( - &schema_for!(CollectionInfo), + &schema_for!(CollectionInfo), &out_dir, "CollectionInfo", ); export_schema(&schema_for!(OwnerOfResponse), &out_dir); - export_schema_with_title( - &schema_for!(NftInfoResponse), - &out_dir, - "NftInfoResponse", - ); export_schema(&schema_for!(NumTokensResponse), &out_dir); export_schema(&schema_for!(TokensResponse), &out_dir); } diff --git a/packages/cw721/schema/all_nft_info_response.json b/packages/cw721/schema/all_nft_info_response.json index d263321e2..8a03851da 100644 --- a/packages/cw721/schema/all_nft_info_response.json +++ b/packages/cw721/schema/all_nft_info_response.json @@ -19,13 +19,17 @@ "description": "Data on the token itself,", "allOf": [ { - "$ref": "#/definitions/NftInfoResponse_for_Nullable_Empty" + "$ref": "#/definitions/NftInfoResponse_for_Nullable_Metadata" } ] } }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -43,15 +47,15 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false }, - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", "oneOf": [ @@ -99,14 +103,77 @@ } ] }, - "NftInfoResponse_for_Nullable_Empty": { + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "NftInfoResponse_for_Nullable_Metadata": { "type": "object", "properties": { "extension": { "description": "You can add any custom metadata here when you extend cw721-base", "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/Metadata" }, { "type": "null" @@ -152,6 +219,28 @@ } ] }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, "Uint64": { "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", "type": "string" diff --git a/packages/cw721/schema/approval_response.json b/packages/cw721/schema/approval_response.json index b29eab59e..c72b71b2d 100644 --- a/packages/cw721/schema/approval_response.json +++ b/packages/cw721/schema/approval_response.json @@ -12,6 +12,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -29,7 +33,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false diff --git a/packages/cw721/schema/approvals_response.json b/packages/cw721/schema/approvals_response.json index 7cdac0015..01caf4d31 100644 --- a/packages/cw721/schema/approvals_response.json +++ b/packages/cw721/schema/approvals_response.json @@ -15,6 +15,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -32,7 +36,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false diff --git a/packages/cw721/schema/collection_info.json b/packages/cw721/schema/collection_info.json index 431c4d037..bae78d23a 100644 --- a/packages/cw721/schema/collection_info.json +++ b/packages/cw721/schema/collection_info.json @@ -11,7 +11,7 @@ "extension": { "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" }, { "type": "null" @@ -30,9 +30,77 @@ }, "additionalProperties": false, "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false }, "Timestamp": { "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", diff --git a/packages/cw721/schema/cw721_execute_msg.json b/packages/cw721/schema/cw721_execute_msg.json deleted file mode 100644 index 228b5c249..000000000 --- a/packages/cw721/schema/cw721_execute_msg.json +++ /dev/null @@ -1,265 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Cw721ExecuteMsg", - "oneOf": [ - { - "description": "Transfer is a base message to move a token to another account without triggering actions", - "type": "object", - "required": [ - "transfer_nft" - ], - "properties": { - "transfer_nft": { - "type": "object", - "required": [ - "recipient", - "token_id" - ], - "properties": { - "recipient": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Send is a base message to transfer a token to a contract and trigger an action on the receiving contract.", - "type": "object", - "required": [ - "send_nft" - ], - "properties": { - "send_nft": { - "type": "object", - "required": [ - "contract", - "msg", - "token_id" - ], - "properties": { - "contract": { - "type": "string" - }, - "msg": { - "$ref": "#/definitions/Binary" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Allows operator to transfer / send the token from the owner's account. If expiration is set, then this allowance has a time/height limit", - "type": "object", - "required": [ - "approve" - ], - "properties": { - "approve": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "expires": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Remove previously granted Approval", - "type": "object", - "required": [ - "revoke" - ], - "properties": { - "revoke": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Allows operator to transfer / send any token from the owner's account. If expiration is set, then this allowance has a time/height limit", - "type": "object", - "required": [ - "approve_all" - ], - "properties": { - "approve_all": { - "type": "object", - "required": [ - "operator" - ], - "properties": { - "expires": { - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "operator": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Remove previously granted ApproveAll permission", - "type": "object", - "required": [ - "revoke_all" - ], - "properties": { - "revoke_all": { - "type": "object", - "required": [ - "operator" - ], - "properties": { - "operator": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Burn an NFT the sender has access to", - "type": "object", - "required": [ - "burn" - ], - "properties": { - "burn": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Binary": { - "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", - "type": "string" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } -} diff --git a/packages/cw721/schema/cw721_query_msg.json b/packages/cw721/schema/cw721_query_msg.json deleted file mode 100644 index 8d7c18acc..000000000 --- a/packages/cw721/schema/cw721_query_msg.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Cw721QueryMsg", - "oneOf": [ - { - "description": "Return the owner of the given token, error if token does not exist Return type: OwnerOfResponse", - "type": "object", - "required": [ - "owner_of" - ], - "properties": { - "owner_of": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return operator that can access all of the owner's tokens. Return type: `ApprovalResponse`", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return approvals that a token has Return type: `ApprovalsResponse`", - "type": "object", - "required": [ - "approvals" - ], - "properties": { - "approvals": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return approval of a given operator for all tokens of an owner, error if not set Return type: `OperatorResponse`", - "type": "object", - "required": [ - "operator" - ], - "properties": { - "operator": { - "type": "object", - "required": [ - "operator", - "owner" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "operator": { - "type": "string" - }, - "owner": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "List all operators that can access all of the owner's tokens Return type: `OperatorsResponse`", - "type": "object", - "required": [ - "all_operators" - ], - "properties": { - "all_operators": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired items, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Total number of tokens issued", - "type": "object", - "required": [ - "num_tokens" - ], - "properties": { - "num_tokens": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns top-level metadata about the contract: `ContractInfoResponse`", - "type": "object", - "required": [ - "contract_info" - ], - "properties": { - "contract_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract: `NftInfoResponse`", - "type": "object", - "required": [ - "nft_info" - ], - "properties": { - "nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization for clients: `AllNftInfo`", - "type": "object", - "required": [ - "all_nft_info" - ], - "properties": { - "all_nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset. Return type: TokensResponse.", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With Enumerable extension. Requires pagination. Lists all token_ids controlled by the contract. Return type: TokensResponse.", - "type": "object", - "required": [ - "all_tokens" - ], - "properties": { - "all_tokens": { - "type": "object", - "properties": { - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] -} diff --git a/packages/cw721/schema/execute_msg.json b/packages/cw721/schema/execute_msg.json new file mode 100644 index 000000000..549baff69 --- /dev/null +++ b/packages/cw721/schema/execute_msg.json @@ -0,0 +1,707 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "deprecated": true, + "type": "object", + "required": [ + "update_ownership" + ], + "properties": { + "update_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_minter_ownership" + ], + "properties": { + "update_minter_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_creator_ownership" + ], + "properties": { + "update_creator_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "description": "Update the collection, can only called by the contract creator", + "type": "object", + "required": [ + "update_collection_info" + ], + "properties": { + "update_collection_info": { + "type": "object", + "required": [ + "collection_info" + ], + "properties": { + "collection_info": { + "$ref": "#/definitions/CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Transfer is a base message to move a token to another account without triggering actions", + "type": "object", + "required": [ + "transfer_nft" + ], + "properties": { + "transfer_nft": { + "type": "object", + "required": [ + "recipient", + "token_id" + ], + "properties": { + "recipient": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Send is a base message to transfer a token to a contract and trigger an action on the receiving contract.", + "type": "object", + "required": [ + "send_nft" + ], + "properties": { + "send_nft": { + "type": "object", + "required": [ + "contract", + "msg", + "token_id" + ], + "properties": { + "contract": { + "type": "string" + }, + "msg": { + "$ref": "#/definitions/Binary" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Allows operator to transfer / send the token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "type": "object", + "required": [ + "approve" + ], + "properties": { + "approve": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Remove previously granted Approval", + "type": "object", + "required": [ + "revoke" + ], + "properties": { + "revoke": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Allows operator to transfer / send any token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "type": "object", + "required": [ + "approve_all" + ], + "properties": { + "approve_all": { + "type": "object", + "required": [ + "operator" + ], + "properties": { + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "operator": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Remove previously granted ApproveAll permission", + "type": "object", + "required": [ + "revoke_all" + ], + "properties": { + "revoke_all": { + "type": "object", + "required": [ + "operator" + ], + "properties": { + "operator": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Mint a new NFT, can only be called by the contract minter", + "type": "object", + "required": [ + "mint" + ], + "properties": { + "mint": { + "type": "object", + "required": [ + "owner", + "token_id" + ], + "properties": { + "extension": { + "description": "Any custom extension used by this contract", + "anyOf": [ + { + "$ref": "#/definitions/Metadata" + }, + { + "type": "null" + } + ] + }, + "owner": { + "description": "The owner of the newly minter NFT", + "type": "string" + }, + "token_id": { + "description": "Unique ID of the NFT", + "type": "string" + }, + "token_uri": { + "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Burn an NFT the sender has access to", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Extension msg", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Sets address to send withdrawn fees to. Only owner can call this.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Removes the withdraw address, so fees are sent to the contract. Only owner can call this.", + "type": "object", + "required": [ + "remove_withdraw_address" + ], + "properties": { + "remove_withdraw_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Withdraw from the contract to the given address. Anyone can call this, which is okay since withdraw address has been set by owner.", + "type": "object", + "required": [ + "withdraw_funds" + ], + "properties": { + "withdraw_funds": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Action": { + "description": "Actions that can be taken to alter the contract's ownership", + "oneOf": [ + { + "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", + "type": "object", + "required": [ + "transfer_ownership" + ], + "properties": { + "transfer_ownership": { + "type": "object", + "required": [ + "new_owner" + ], + "properties": { + "expiry": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "new_owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", + "type": "string", + "enum": [ + "accept_ownership" + ] + }, + { + "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", + "type": "string", + "enum": [ + "renounce_ownership" + ] + } + ] + }, + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "name", + "symbol" + ], + "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/packages/cw721/schema/instantiate_msg.json b/packages/cw721/schema/instantiate_msg.json new file mode 100644 index 000000000..660e888ab --- /dev/null +++ b/packages/cw721/schema/instantiate_msg.json @@ -0,0 +1,136 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "name", + "symbol" + ], + "properties": { + "collection_info_extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "creator": { + "description": "The creator is the only who can update collection info.", + "type": [ + "string", + "null" + ] + }, + "minter": { + "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", + "type": [ + "string", + "null" + ] + }, + "name": { + "description": "Name of the NFT contract", + "type": "string" + }, + "symbol": { + "description": "Symbol of the NFT contract", + "type": "string" + }, + "withdraw_address": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/packages/cw721/schema/nft_info_response.json b/packages/cw721/schema/nft_info_response.json index 51b1f072c..0f0859230 100644 --- a/packages/cw721/schema/nft_info_response.json +++ b/packages/cw721/schema/nft_info_response.json @@ -7,7 +7,7 @@ "description": "You can add any custom metadata here when you extend cw721-base", "anyOf": [ { - "$ref": "#/definitions/Empty" + "$ref": "#/definitions/Metadata" }, { "type": "null" @@ -24,9 +24,90 @@ }, "additionalProperties": false, "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false } } } diff --git a/packages/cw721/schema/operator_response.json b/packages/cw721/schema/operator_response.json index 9e2dbd3d2..097e08213 100644 --- a/packages/cw721/schema/operator_response.json +++ b/packages/cw721/schema/operator_response.json @@ -12,6 +12,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -29,7 +33,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false diff --git a/packages/cw721/schema/operators_response.json b/packages/cw721/schema/operators_response.json index 533a096dd..426cd48cc 100644 --- a/packages/cw721/schema/operators_response.json +++ b/packages/cw721/schema/operators_response.json @@ -15,6 +15,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -32,7 +36,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false diff --git a/packages/cw721/schema/owner_of_response.json b/packages/cw721/schema/owner_of_response.json index abb9006d8..36b85a171 100644 --- a/packages/cw721/schema/owner_of_response.json +++ b/packages/cw721/schema/owner_of_response.json @@ -21,6 +21,10 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Approval": { "type": "object", "required": [ @@ -38,7 +42,11 @@ }, "spender": { "description": "Account that can transfer/send the token", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] } }, "additionalProperties": false diff --git a/packages/cw721/schema/query_msg.json b/packages/cw721/schema/query_msg.json new file mode 100644 index 000000000..f587f0445 --- /dev/null +++ b/packages/cw721/schema/query_msg.json @@ -0,0 +1,535 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "description": "Return the owner of the given token, error if token does not exist", + "type": "object", + "required": [ + "owner_of" + ], + "properties": { + "owner_of": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "description": "unset or false will filter out expired approvals, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return operator that can access all of the owner's tokens.", + "type": "object", + "required": [ + "approval" + ], + "properties": { + "approval": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return approvals that a token has", + "type": "object", + "required": [ + "approvals" + ], + "properties": { + "approvals": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return approval of a given operator for all tokens of an owner, error if not set", + "type": "object", + "required": [ + "operator" + ], + "properties": { + "operator": { + "type": "object", + "required": [ + "operator", + "owner" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "operator": { + "type": "string" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "List all operators that can access all of the owner's tokens", + "type": "object", + "required": [ + "all_operators" + ], + "properties": { + "all_operators": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "include_expired": { + "description": "unset or false will filter out expired items, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Total number of tokens issued", + "type": "object", + "required": [ + "num_tokens" + ], + "properties": { + "num_tokens": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "deprecated": true, + "type": "object", + "required": [ + "contract_info" + ], + "properties": { + "contract_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With MetaData Extension. Returns top-level metadata about the contract", + "type": "object", + "required": [ + "get_collection_info" + ], + "properties": { + "get_collection_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "deprecated": true, + "type": "object", + "required": [ + "ownership" + ], + "properties": { + "ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_minter_ownership" + ], + "properties": { + "get_minter_ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_creator_ownership" + ], + "properties": { + "get_creator_ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", + "type": "object", + "required": [ + "nft_info" + ], + "properties": { + "nft_info": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With MetaData Extension. Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization for clients", + "type": "object", + "required": [ + "all_nft_info" + ], + "properties": { + "all_nft_info": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "description": "unset or false will filter out expired approvals, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", + "type": "object", + "required": [ + "tokens" + ], + "properties": { + "tokens": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With Enumerable extension. Requires pagination. Lists all token_ids controlled by the contract.", + "type": "object", + "required": [ + "all_tokens" + ], + "properties": { + "all_tokens": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return the minter", + "deprecated": true, + "type": "object", + "required": [ + "minter" + ], + "properties": { + "minter": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Extension query", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg`", + "type": "object", + "required": [ + "get_collection_info_extension" + ], + "properties": { + "get_collection_info_extension": { + "type": "object", + "properties": { + "msg": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_withdraw_address" + ], + "properties": { + "get_withdraw_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CollectionInfoExtension_for_RoyaltyInfo": { + "type": "object", + "required": [ + "description", + "image" + ], + "properties": { + "description": { + "type": "string" + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": "string" + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "RoyaltyInfo": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "$ref": "#/definitions/Addr" + }, + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/packages/cw721/src/error.rs b/packages/cw721/src/error.rs new file mode 100644 index 000000000..99e274b3f --- /dev/null +++ b/packages/cw721/src/error.rs @@ -0,0 +1,27 @@ +use cosmwasm_std::StdError; +use cw_ownable::OwnershipError; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum Cw721ContractError { + #[error(transparent)] + Std(#[from] StdError), + + #[error(transparent)] + Ownership(#[from] OwnershipError), + + #[error(transparent)] + Version(#[from] cw2::VersionError), + + #[error("token_id already claimed")] + Claimed {}, + + #[error("Cannot set approval that is already expired")] + Expired {}, + + #[error("Approval not found for: {spender}")] + ApprovalNotFound { spender: String }, + + #[error("No withdraw address set")] + NoWithdrawAddress {}, +} diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs new file mode 100644 index 000000000..773106fe4 --- /dev/null +++ b/packages/cw721/src/execute.rs @@ -0,0 +1,810 @@ +use cosmwasm_std::{ + Addr, Api, BankMsg, Binary, Coin, CustomMsg, Deps, DepsMut, Empty, Env, MessageInfo, Order, + Response, StdResult, Storage, +}; +use cw_ownable::{none_or, Action, Ownership, OwnershipError}; +use cw_storage_plus::{IndexedMap, Item, MultiIndex}; +use cw_utils::Expiration; +use serde::de::DeserializeOwned; +use serde::Serialize; + +use crate::{ + error::Cw721ContractError, + msg::{CollectionInfoMsg, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg}, + receiver::Cw721ReceiveMsg, + state::{ + token_owner_idx, CollectionInfo, Cw721Config, DefaultOptionCollectionInfoExtension, + DefaultOptionMetadataExtension, NftInfo, TokenIndexes, CREATOR, MINTER, + }, + Approval, +}; + +pub trait Cw721Execute< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, +> where + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ + fn instantiate( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: Cw721InstantiateMsg, + contract_name: &str, + contract_version: &str, + ) -> Result, Cw721ContractError> { + cw2::set_contract_version(deps.storage, contract_name, contract_version)?; + let config = Cw721Config::::default(); + let collection_info = CollectionInfo { + name: msg.name, + symbol: msg.symbol, + extension: msg.collection_info_extension, + updated_at: env.block.time, + }; + config + .collection_info + .save(deps.storage, &collection_info)?; + + // use info.sender if None is passed + let minter: &str = match msg.minter.as_deref() { + Some(minter) => minter, + None => info.sender.as_str(), + }; + self.initialize_minter(deps.storage, deps.api, Some(minter))?; + + // use info.sender if None is passed + let creator: &str = match msg.creator.as_deref() { + Some(creator) => creator, + None => info.sender.as_str(), + }; + self.initialize_creator(deps.storage, deps.api, Some(creator))?; + + if let Some(withdraw_address) = msg.withdraw_address { + let creator = deps.api.addr_validate(creator)?; + self.set_withdraw_address(deps, &creator, withdraw_address)?; + } + + Ok(Response::default() + .add_attribute("minter", minter) + .add_attribute("creator", creator)) + } + + fn execute( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: Cw721ExecuteMsg, + ) -> Result, Cw721ContractError> { + match msg { + Cw721ExecuteMsg::UpdateCollectionInfo { collection_info } => { + self.update_collection_info(deps, info, env, collection_info) + } + Cw721ExecuteMsg::Mint { + token_id, + owner, + token_uri, + extension, + } => self.mint(deps, info, token_id, owner, token_uri, extension), + Cw721ExecuteMsg::Approve { + spender, + token_id, + expires, + } => self.approve(deps, env, info, spender, token_id, expires), + Cw721ExecuteMsg::Revoke { spender, token_id } => { + self.revoke(deps, env, info, spender, token_id) + } + Cw721ExecuteMsg::ApproveAll { operator, expires } => { + self.approve_all(deps, env, info, operator, expires) + } + Cw721ExecuteMsg::RevokeAll { operator } => self.revoke_all(deps, env, info, operator), + Cw721ExecuteMsg::TransferNft { + recipient, + token_id, + } => self.transfer_nft(deps, env, info, recipient, token_id), + Cw721ExecuteMsg::SendNft { + contract, + token_id, + msg, + } => self.send_nft(deps, env, info, contract, token_id, msg), + Cw721ExecuteMsg::Burn { token_id } => self.burn_nft(deps, env, info, token_id), + #[allow(deprecated)] + Cw721ExecuteMsg::UpdateOwnership(action) => { + self.update_minter_ownership(deps, env, info, action) + } + Cw721ExecuteMsg::UpdateMinterOwnership(action) => { + self.update_minter_ownership(deps, env, info, action) + } + Cw721ExecuteMsg::UpdateCreatorOwnership(action) => { + self.update_creator_ownership(deps, env, info, action) + } + Cw721ExecuteMsg::Extension { msg: _ } => Ok(Response::default()), + Cw721ExecuteMsg::SetWithdrawAddress { address } => { + self.set_withdraw_address(deps, &info.sender, address) + } + Cw721ExecuteMsg::RemoveWithdrawAddress {} => { + self.remove_withdraw_address(deps.storage, &info.sender) + } + Cw721ExecuteMsg::WithdrawFunds { amount } => self.withdraw_funds(deps.storage, &amount), + } + } + + fn migrate( + &self, + deps: DepsMut, + env: Env, + msg: Cw721MigrateMsg, + contract_name: &str, + contract_version: &str, + ) -> Result { + let response = Response::::default(); + // first migrate legacy data ... + let response = + migrate_legacy_minter_and_creator(deps.storage, deps.api, &env, &msg, response)?; + let response = migrate_legacy_collection_info(deps.storage, &env, &msg, response)?; + let response = migrate_legacy_tokens(deps.storage, &env, &msg, response)?; + // ... then migrate + let response = migrate_version(deps.storage, contract_name, contract_version, response)?; + // ... and update creator and minter AFTER legacy migration + let response = migrate_creator(deps.storage, deps.api, &env, &msg, response)?; + let response = migrate_minter(deps.storage, deps.api, &env, &msg, response)?; + Ok(response) + } + + // ------- ERC721-based functions ------- + fn transfer_nft( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + recipient: String, + token_id: String, + ) -> Result, Cw721ContractError> { + _transfer_nft::(deps, &env, &info, &recipient, &token_id)?; + + Ok(Response::new() + .add_attribute("action", "transfer_nft") + .add_attribute("sender", info.sender) + .add_attribute("recipient", recipient) + .add_attribute("token_id", token_id)) + } + + fn send_nft( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + contract: String, + token_id: String, + msg: Binary, + ) -> Result, Cw721ContractError> { + // Transfer token + _transfer_nft::(deps, &env, &info, &contract, &token_id)?; + + let send = Cw721ReceiveMsg { + sender: info.sender.to_string(), + token_id: token_id.clone(), + msg, + }; + + // Send message + Ok(Response::new() + .add_message(send.into_cosmos_msg(contract.clone())?) + .add_attribute("action", "send_nft") + .add_attribute("sender", info.sender) + .add_attribute("recipient", contract) + .add_attribute("token_id", token_id)) + } + + fn approve( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + spender: String, + token_id: String, + expires: Option, + ) -> Result, Cw721ContractError> { + _update_approvals::(deps, &env, &info, &spender, &token_id, true, expires)?; + + Ok(Response::new() + .add_attribute("action", "approve") + .add_attribute("sender", info.sender) + .add_attribute("spender", spender) + .add_attribute("token_id", token_id)) + } + + fn revoke( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + spender: String, + token_id: String, + ) -> Result, Cw721ContractError> { + _update_approvals::(deps, &env, &info, &spender, &token_id, false, None)?; + + Ok(Response::new() + .add_attribute("action", "revoke") + .add_attribute("sender", info.sender) + .add_attribute("spender", spender) + .add_attribute("token_id", token_id)) + } + + fn approve_all( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + operator: String, + expires: Option, + ) -> Result, Cw721ContractError> { + // reject expired data as invalid + let expires = expires.unwrap_or_default(); + if expires.is_expired(&env.block) { + return Err(Cw721ContractError::Expired {}); + } + + // set the operator for us + let operator_addr = deps.api.addr_validate(&operator)?; + let config = Cw721Config::< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + >::default(); + config + .operators + // stores info.sender as key (=granter, NFT owner) and operator as value (operator only(!) has control over NFTs of granter) + // check is done in `check_can_send()` + .save(deps.storage, (&info.sender, &operator_addr), &expires)?; + + Ok(Response::new() + .add_attribute("action", "approve_all") + .add_attribute("sender", info.sender) + .add_attribute("operator", operator)) + } + + fn revoke_all( + &self, + deps: DepsMut, + _env: Env, + info: MessageInfo, + operator: String, + ) -> Result, Cw721ContractError> { + let operator_addr = deps.api.addr_validate(&operator)?; + let config = Cw721Config::< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + >::default(); + config + .operators + .remove(deps.storage, (&info.sender, &operator_addr)); + + Ok(Response::new() + .add_attribute("action", "revoke_all") + .add_attribute("sender", info.sender) + .add_attribute("operator", operator)) + } + + fn burn_nft( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + token_id: String, + ) -> Result, Cw721ContractError> { + let config = Cw721Config::< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + >::default(); + let token = config.nft_info.load(deps.storage, &token_id)?; + check_can_send(deps.as_ref(), &env, &info, &token)?; + + config.nft_info.remove(deps.storage, &token_id)?; + config.decrement_tokens(deps.storage)?; + + Ok(Response::new() + .add_attribute("action", "burn") + .add_attribute("sender", info.sender) + .add_attribute("token_id", token_id)) + } + + // ------- opionated cw721 functions ------- + fn initialize_creator( + &self, + storage: &mut dyn Storage, + api: &dyn Api, + creator: Option<&str>, + ) -> StdResult> { + CREATOR.initialize_owner(storage, api, creator) + } + + fn initialize_minter( + &self, + storage: &mut dyn Storage, + api: &dyn Api, + minter: Option<&str>, + ) -> StdResult> { + MINTER.initialize_owner(storage, api, minter) + } + + fn update_collection_info( + &self, + deps: DepsMut, + info: MessageInfo, + env: Env, + msg: CollectionInfoMsg, + ) -> Result, Cw721ContractError> { + CREATOR.assert_owner(deps.storage, &info.sender)?; + let collection_info = CollectionInfo { + name: msg.name, + symbol: msg.symbol, + extension: msg.extension, + updated_at: env.block.time, + }; + let config = Cw721Config::< + Empty, + TCustomResponseMessage, + Empty, + Empty, + TCollectionInfoExtension, + >::default(); + config + .collection_info + .save(deps.storage, &collection_info)?; + + Ok(Response::new() + .add_attribute("action", "update_collection_info") + .add_attribute("sender", info.sender)) + } + + fn mint( + &self, + deps: DepsMut, + info: MessageInfo, + token_id: String, + owner: String, + token_uri: Option, + extension: TMetadata, + ) -> Result, Cw721ContractError> { + MINTER.assert_owner(deps.storage, &info.sender)?; + + // create the token + let token = NftInfo { + owner: deps.api.addr_validate(&owner)?, + approvals: vec![], + token_uri, + extension, + }; + let config = Cw721Config::::default(); + config + .nft_info + .update(deps.storage, &token_id, |old| match old { + Some(_) => Err(Cw721ContractError::Claimed {}), + None => Ok(token), + })?; + + config.increment_tokens(deps.storage)?; + + Ok(Response::new() + .add_attribute("action", "mint") + .add_attribute("minter", info.sender) + .add_attribute("owner", owner) + .add_attribute("token_id", token_id)) + } + + fn update_minter_ownership( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + action: Action, + ) -> Result, Cw721ContractError> { + let ownership = MINTER.update_ownership(deps, &env.block, &info.sender, action)?; + Ok(Response::new() + .add_attribute("update_minter_ownership", info.sender) + .add_attributes(ownership.into_attributes())) + } + + fn update_creator_ownership( + &self, + deps: DepsMut, + env: Env, + info: MessageInfo, + action: Action, + ) -> Result, Cw721ContractError> { + let ownership = CREATOR.update_ownership(deps, &env.block, &info.sender, action)?; + Ok(Response::new() + .add_attribute("update_creator_ownership", info.sender) + .add_attributes(ownership.into_attributes())) + } + + fn set_withdraw_address( + &self, + deps: DepsMut, + sender: &Addr, + address: String, + ) -> Result, Cw721ContractError> { + CREATOR.assert_owner(deps.storage, sender)?; + deps.api.addr_validate(&address)?; + let config = Cw721Config::< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + >::default(); + config.withdraw_address.save(deps.storage, &address)?; + Ok(Response::new() + .add_attribute("action", "set_withdraw_address") + .add_attribute("address", address)) + } + + fn remove_withdraw_address( + &self, + storage: &mut dyn Storage, + sender: &Addr, + ) -> Result, Cw721ContractError> { + CREATOR.assert_owner(storage, sender)?; + let config = Cw721Config::< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + >::default(); + let address = config.withdraw_address.may_load(storage)?; + match address { + Some(address) => { + config.withdraw_address.remove(storage); + Ok(Response::new() + .add_attribute("action", "remove_withdraw_address") + .add_attribute("address", address)) + } + None => Err(Cw721ContractError::NoWithdrawAddress {}), + } + } + + fn withdraw_funds( + &self, + storage: &mut dyn Storage, + amount: &Coin, + ) -> Result, Cw721ContractError> { + let withdraw_address = Cw721Config::< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .withdraw_address + .may_load(storage)?; + match withdraw_address { + Some(address) => { + let msg = BankMsg::Send { + to_address: address, + amount: vec![amount.clone()], + }; + Ok(Response::new() + .add_message(msg) + .add_attribute("action", "withdraw_funds") + .add_attribute("amount", amount.amount.to_string()) + .add_attribute("denom", amount.denom.to_string())) + } + None => Err(Cw721ContractError::NoWithdrawAddress {}), + } + } +} + +// ------- helper cw721 functions ------- +fn _transfer_nft( + deps: DepsMut, + env: &Env, + info: &MessageInfo, + recipient: &str, + token_id: &str, +) -> Result, Cw721ContractError> +where + TMetadata: Serialize + DeserializeOwned + Clone, +{ + let config = Cw721Config::::default(); + let mut token = config.nft_info.load(deps.storage, token_id)?; + // ensure we have permissions + check_can_send(deps.as_ref(), env, info, &token)?; + // set owner and remove existing approvals + token.owner = deps.api.addr_validate(recipient)?; + token.approvals = vec![]; + config.nft_info.save(deps.storage, token_id, &token)?; + Ok(token) +} + +#[allow(clippy::too_many_arguments)] +fn _update_approvals( + deps: DepsMut, + env: &Env, + info: &MessageInfo, + spender: &str, + token_id: &str, + // if add == false, remove. if add == true, remove then set with this expiration + add: bool, + expires: Option, +) -> Result, Cw721ContractError> +where + TMetadata: Serialize + DeserializeOwned + Clone, +{ + let config = Cw721Config::::default(); + let mut token = config.nft_info.load(deps.storage, token_id)?; + // ensure we have permissions + check_can_approve(deps.as_ref(), env, info, &token)?; + + // update the approval list (remove any for the same spender before adding) + let spender_addr = deps.api.addr_validate(spender)?; + token.approvals.retain(|apr| apr.spender != spender_addr); + + // only difference between approve and revoke + if add { + // reject expired data as invalid + let expires = expires.unwrap_or_default(); + if expires.is_expired(&env.block) { + return Err(Cw721ContractError::Expired {}); + } + let approval = Approval { + spender: spender_addr, + expires, + }; + token.approvals.push(approval); + } + + config.nft_info.save(deps.storage, token_id, &token)?; + + Ok(token) +} + +/// returns true if the sender can execute approve or reject on the contract +pub fn check_can_approve( + deps: Deps, + env: &Env, + info: &MessageInfo, + token: &NftInfo, +) -> Result<(), Cw721ContractError> +where + TMetadata: Serialize + DeserializeOwned + Clone, +{ + // owner can approve + if token.owner == info.sender { + return Ok(()); + } + // operator can approve + let config = Cw721Config::::default(); + let op = config + .operators + .may_load(deps.storage, (&token.owner, &info.sender))?; + match op { + Some(ex) => { + if ex.is_expired(&env.block) { + Err(Cw721ContractError::Ownership(OwnershipError::NotOwner)) + } else { + Ok(()) + } + } + None => Err(Cw721ContractError::Ownership(OwnershipError::NotOwner)), + } +} + +/// returns true iff the sender can transfer ownership of the token +pub fn check_can_send( + deps: Deps, + env: &Env, + info: &MessageInfo, + token: &NftInfo, +) -> Result<(), Cw721ContractError> { + // owner can send + if token.owner == info.sender { + return Ok(()); + } + + // any non-expired token approval can send + if token + .approvals + .iter() + .any(|apr| apr.spender == info.sender && !apr.is_expired(&env.block)) + { + return Ok(()); + } + + // operator can send + let config = Cw721Config::::default(); + let op = config + .operators + // has token owner approved/gave grant to sender for full control over owner's NFTs? + .may_load(deps.storage, (&token.owner, &info.sender))?; + + match op { + Some(ex) => { + if ex.is_expired(&env.block) { + Err(Cw721ContractError::Ownership(OwnershipError::NotOwner)) + } else { + Ok(()) + } + } + None => Err(Cw721ContractError::Ownership(OwnershipError::NotOwner)), + } +} + +// ------- migrate ------- +pub fn migrate_version( + storage: &mut dyn Storage, + contradct_name: &str, + contract_version: &str, + response: Response, +) -> StdResult { + let response = response + .add_attribute("from_version", cw2::get_contract_version(storage)?.version) + .add_attribute("to_version", contract_version); + + // update contract version + cw2::set_contract_version(storage, contradct_name, contract_version)?; + Ok(response) +} + +pub fn migrate_creator( + storage: &mut dyn Storage, + api: &dyn Api, + _env: &Env, + msg: &Cw721MigrateMsg, + response: Response, +) -> StdResult { + match msg { + Cw721MigrateMsg::WithUpdate { creator, .. } => { + if let Some(creator) = creator { + CREATOR.initialize_owner(storage, api, Some(creator.as_str()))?; + return Ok(response.add_attribute("creator", creator)); + } + } + } + Ok(response) +} + +pub fn migrate_minter( + storage: &mut dyn Storage, + api: &dyn Api, + _env: &Env, + msg: &Cw721MigrateMsg, + response: Response, +) -> StdResult { + match msg { + Cw721MigrateMsg::WithUpdate { minter, .. } => { + if let Some(minter) = minter { + MINTER.initialize_owner(storage, api, Some(minter.as_str()))?; + return Ok(response.add_attribute("creator", minter)); + } + } + } + Ok(response) +} + +/// Migrates only in case ownership is not present +/// !!! Important note here: !!! +/// - creator owns the contract and can update collection info +/// - minter can mint new tokens +/// +/// Before v0.19.0 there were confusing naming conventions: +/// - v0.17.0: minter was replaced by cw_ownable, as a result minter is owner +/// - v0.16.0 and below: minter was stored in dedicated `minter` store (so NOT using cw_ownable at all) +pub fn migrate_legacy_minter_and_creator( + storage: &mut dyn Storage, + api: &dyn Api, + _env: &Env, + _msg: &Cw721MigrateMsg, + response: Response, +) -> Result { + let minter = MINTER.item.may_load(storage)?; + // no migration in case minter is already set + if minter.is_some() { + return Ok(response); + } + // in v0.17/18 cw_ownable::OWNERSHIP was used for minter, now it is used for creator + let ownership_previously_used_as_minter = CREATOR.item.may_load(storage)?; + let creator_and_minter = match ownership_previously_used_as_minter { + // v0.18 migration + Some(ownership) => { + // owner is used for both: creator and minter + // since it is already set for creator, we only need to migrate minter + let owner = ownership.owner.map(|a| a.to_string()); + MINTER.initialize_owner(storage, api, owner.as_deref())?; + owner + } + // v0.17 and older migration + None => { + let legacy_minter_store: Item = Item::new("minter"); + let legacy_minter = legacy_minter_store.load(storage)?; + MINTER.initialize_owner(storage, api, Some(legacy_minter.as_str()))?; + CREATOR.initialize_owner(storage, api, Some(legacy_minter.as_str()))?; + Some(legacy_minter.to_string()) + } + }; + Ok(response.add_attribute("creator_and_minter", none_or(creator_and_minter.as_ref()))) +} + +/// Migrates only in case collection_info is not present +pub fn migrate_legacy_collection_info( + storage: &mut dyn Storage, + env: &Env, + _msg: &Cw721MigrateMsg, + response: Response, +) -> Result { + let contract = Cw721Config::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + match contract.collection_info.may_load(storage)? { + Some(_) => Ok(response), + None => { + // contract info is legacy collection info + let legacy_collection_info_store: Item = + Item::new("nft_info"); + let legacy_collection_info = legacy_collection_info_store.load(storage)?; + let collection_info = CollectionInfo { + name: legacy_collection_info.name.clone(), + symbol: legacy_collection_info.symbol.clone(), + extension: None, + updated_at: env.block.time, + }; + contract.collection_info.save(storage, &collection_info)?; + Ok(response + .add_attribute("migrated collection name", legacy_collection_info.name) + .add_attribute("migrated collection symbol", legacy_collection_info.symbol)) + } + } +} + +/// Migrates only in case collection_info is not present +pub fn migrate_legacy_tokens( + storage: &mut dyn Storage, + _env: &Env, + _msg: &Cw721MigrateMsg, + response: Response, +) -> StdResult { + let contract = + Cw721Config::::default(); + match contract.nft_info.is_empty(storage) { + false => Ok(response), + true => { + let indexes = TokenIndexes { + owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"), + }; + let legacy_tokens_store: IndexedMap< + &str, + NftInfo, + TokenIndexes, + > = IndexedMap::new("tokens", indexes); + let keys = legacy_tokens_store + .keys(storage, None, None, Order::Ascending) + .collect::>>()?; + for key in keys { + let legacy_token = legacy_tokens_store.load(storage, &key)?; + contract.nft_info.save(storage, &key, &legacy_token)?; + } + Ok(response) + } + } +} diff --git a/contracts/cw721-base/src/helpers.rs b/packages/cw721/src/helpers.rs similarity index 83% rename from contracts/cw721-base/src/helpers.rs rename to packages/cw721/src/helpers.rs index 288ea831d..17fbc145b 100644 --- a/contracts/cw721-base/src/helpers.rs +++ b/packages/cw721/src/helpers.rs @@ -1,19 +1,20 @@ use std::marker::PhantomData; +use crate::msg::{Cw721ExecuteMsg, Cw721QueryMsg}; +use crate::query::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, NumTokensResponse, + OperatorsResponse, OwnerOfResponse, TokensResponse, +}; +use crate::state::CollectionInfo; +use crate::Approval; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ to_json_binary, Addr, CosmosMsg, CustomMsg, Empty, QuerierWrapper, StdResult, WasmMsg, WasmQuery, }; -use cw721::{ - AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, CollectionInfo, - NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, -}; use serde::de::DeserializeOwned; use serde::Serialize; -use crate::{ExecuteMsg, QueryMsg}; - #[cw_serde] pub struct Cw721Contract< TMetadataResponse: CustomMsg, @@ -29,6 +30,8 @@ pub struct Cw721Contract< #[allow(dead_code)] impl Cw721Contract +where + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub fn addr(&self) -> Addr { self.0.clone() @@ -36,7 +39,7 @@ impl( &self, - msg: ExecuteMsg, + msg: Cw721ExecuteMsg, ) -> StdResult { let msg = to_json_binary(&msg)?; Ok(WasmMsg::Execute { @@ -50,7 +53,7 @@ impl( &self, querier: &QuerierWrapper, - req: QueryMsg, + req: Cw721QueryMsg, ) -> StdResult { let query = WasmQuery::Smart { contract_addr: self.addr().into(), @@ -68,7 +71,7 @@ impl StdResult { - let req = QueryMsg::OwnerOf { + let req = Cw721QueryMsg::OwnerOf { token_id: token_id.into(), include_expired: Some(include_expired), }; @@ -82,7 +85,7 @@ impl, ) -> StdResult { - let req = QueryMsg::Approval { + let req = Cw721QueryMsg::Approval { token_id: token_id.into(), spender: spender.into(), include_expired, @@ -97,7 +100,7 @@ impl, ) -> StdResult { - let req = QueryMsg::Approvals { + let req = Cw721QueryMsg::Approvals { token_id: token_id.into(), include_expired, }; @@ -113,7 +116,7 @@ impl, limit: Option, ) -> StdResult> { - let req = QueryMsg::AllOperators { + let req = Cw721QueryMsg::AllOperators { owner: owner.into(), include_expired: Some(include_expired), start_after, @@ -124,7 +127,7 @@ impl StdResult { - let req = QueryMsg::NumTokens {}; + let req = Cw721QueryMsg::NumTokens {}; let res: NumTokensResponse = self.query(querier, req)?; Ok(res.count) } @@ -134,7 +137,7 @@ impl StdResult> { - let req = QueryMsg::GetCollectionInfo {}; + let req = Cw721QueryMsg::GetCollectionInfo {}; self.query(querier, req) } @@ -144,7 +147,7 @@ impl StdResult> { - let req = QueryMsg::NftInfo { + let req = Cw721QueryMsg::NftInfo { token_id: token_id.into(), }; self.query(querier, req) @@ -157,7 +160,7 @@ impl StdResult> { - let req = QueryMsg::AllNftInfo { + let req = Cw721QueryMsg::AllNftInfo { token_id: token_id.into(), include_expired: Some(include_expired), }; @@ -172,7 +175,7 @@ impl, limit: Option, ) -> StdResult { - let req = QueryMsg::Tokens { + let req = Cw721QueryMsg::Tokens { owner: owner.into(), start_after, limit, @@ -187,7 +190,7 @@ impl, limit: Option, ) -> StdResult { - let req = QueryMsg::AllTokens { start_after, limit }; + let req = Cw721QueryMsg::AllTokens { start_after, limit }; self.query(querier, req) } diff --git a/packages/cw721/src/lib.rs b/packages/cw721/src/lib.rs index 378eb9430..0c0ff16e5 100644 --- a/packages/cw721/src/lib.rs +++ b/packages/cw721/src/lib.rs @@ -1,23 +1,13 @@ -mod msg; -mod query; -mod receiver; -mod state; -mod traits; +pub mod error; +pub mod execute; +pub mod helpers; +pub mod msg; +pub mod query; +pub mod receiver; +pub mod state; -use cosmwasm_std::Empty; pub use cw_utils::Expiration; +pub use state::{Approval, CollectionInfoExtension, RoyaltyInfo}; -pub use crate::msg::Cw721ExecuteMsg; -#[allow(deprecated)] -pub use crate::query::{ - AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, Cw721QueryMsg, - NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, - TokensResponse, -}; -pub use crate::receiver::Cw721ReceiveMsg; -pub use crate::state::{CollectionInfo, Metadata, MetadataExtension, Trait}; -pub use crate::traits::{Cw721, Cw721Execute, Cw721Query}; - -// These are simple types to let us handle empty extensions -pub type EmptyExtension = Option; -pub type EmptyCollectionInfoExtension = Option; +#[cfg(test)] +pub mod testing; diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index 87ce6cea3..27f50715a 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -1,11 +1,34 @@ -use cosmwasm_schema::cw_serde; -use cosmwasm_std::Binary; +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Addr, Binary, Coin}; +use cw_ownable::{Action, Ownership}; use cw_utils::Expiration; +use schemars::JsonSchema; + +use crate::query::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, NumTokensResponse, + OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, +}; + +use crate::state::CollectionInfo; + +use cosmwasm_std::Empty; #[cw_serde] -pub enum Cw721ExecuteMsg { +pub enum Cw721ExecuteMsg { + #[deprecated(since = "0.19.0", note = "Please use UpdateMinterOwnership instead")] + UpdateOwnership(Action), + UpdateMinterOwnership(Action), + UpdateCreatorOwnership(Action), + + /// Update the collection, can only called by the contract creator + UpdateCollectionInfo { + collection_info: CollectionInfoMsg, + }, /// Transfer is a base message to move a token to another account without triggering actions - TransferNft { recipient: String, token_id: String }, + TransferNft { + recipient: String, + token_id: String, + }, /// Send is a base message to transfer a token to a contract and trigger an action /// on the receiving contract. SendNft { @@ -21,7 +44,10 @@ pub enum Cw721ExecuteMsg { expires: Option, }, /// Remove previously granted Approval - Revoke { spender: String, token_id: String }, + Revoke { + spender: String, + token_id: String, + }, /// Allows operator to transfer / send any token from the owner's account. /// If expiration is set, then this allowance has a time/height limit ApproveAll { @@ -29,7 +55,195 @@ pub enum Cw721ExecuteMsg { expires: Option, }, /// Remove previously granted ApproveAll permission - RevokeAll { operator: String }, + RevokeAll { + operator: String, + }, + + /// Mint a new NFT, can only be called by the contract minter + Mint { + /// Unique ID of the NFT + token_id: String, + /// The owner of the newly minter NFT + owner: String, + /// Universal resource identifier for this NFT + /// Should point to a JSON file that conforms to the ERC721 + /// Metadata JSON Schema + token_uri: Option, + /// Any custom extension used by this contract + extension: TMetadata, + }, + /// Burn an NFT the sender has access to - Burn { token_id: String }, + Burn { + token_id: String, + }, + + /// Extension msg + Extension { + msg: TExtensionExecuteMsg, + }, + + /// Sets address to send withdrawn fees to. Only owner can call this. + SetWithdrawAddress { + address: String, + }, + /// Removes the withdraw address, so fees are sent to the contract. Only owner can call this. + RemoveWithdrawAddress {}, + /// Withdraw from the contract to the given address. Anyone can call this, + /// which is okay since withdraw address has been set by owner. + WithdrawFunds { + amount: Coin, + }, +} + +#[cw_serde] +pub struct Cw721InstantiateMsg { + /// Name of the NFT contract + pub name: String, + /// Symbol of the NFT contract + pub symbol: String, + + pub collection_info_extension: TCollectionInfoExtension, + + /// The minter is the only one who can create new NFTs. + /// This is designed for a base NFT that is controlled by an external program + /// or contract. You will likely replace this with custom logic in custom NFTs + pub minter: Option, + + /// The creator is the only who can update collection info. + pub creator: Option, + + pub withdraw_address: Option, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum Cw721QueryMsg { + /// Return the owner of the given token, error if token does not exist + #[returns(OwnerOfResponse)] + OwnerOf { + token_id: String, + /// unset or false will filter out expired approvals, you must set to true to see them + include_expired: Option, + }, + /// Return operator that can access all of the owner's tokens. + #[returns(ApprovalResponse)] + Approval { + token_id: String, + spender: String, + include_expired: Option, + }, + /// Return approvals that a token has + #[returns(ApprovalsResponse)] + Approvals { + token_id: String, + include_expired: Option, + }, + /// Return approval of a given operator for all tokens of an owner, error if not set + #[returns(OperatorResponse)] + Operator { + owner: String, + operator: String, + include_expired: Option, + }, + /// List all operators that can access all of the owner's tokens + #[returns(OperatorsResponse)] + AllOperators { + owner: String, + /// unset or false will filter out expired items, you must set to true to see them + include_expired: Option, + start_after: Option, + limit: Option, + }, + /// Total number of tokens issued + #[returns(NumTokensResponse)] + NumTokens {}, + + #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] + #[returns(CollectionInfo)] + ContractInfo {}, + + /// With MetaData Extension. + /// Returns top-level metadata about the contract + #[returns(CollectionInfo)] + GetCollectionInfo {}, + + #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] + #[returns(Ownership)] + Ownership {}, + + #[returns(Ownership)] + GetMinterOwnership {}, + + #[returns(Ownership)] + GetCreatorOwnership {}, + + /// With MetaData Extension. + /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* + /// but directly from the contract + #[returns(NftInfoResponse)] + NftInfo { token_id: String }, + /// With MetaData Extension. + /// Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization + /// for clients + #[returns(AllNftInfoResponse)] + AllNftInfo { + token_id: String, + /// unset or false will filter out expired approvals, you must set to true to see them + include_expired: Option, + }, + + /// With Enumerable extension. + /// Returns all tokens owned by the given address, [] if unset. + #[returns(TokensResponse)] + Tokens { + owner: String, + start_after: Option, + limit: Option, + }, + /// With Enumerable extension. + /// Requires pagination. Lists all token_ids controlled by the contract. + #[returns(TokensResponse)] + AllTokens { + start_after: Option, + limit: Option, + }, + + /// Return the minter + #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] + #[returns(MinterResponse)] + Minter {}, + + /// Extension query + #[returns(())] + Extension { msg: TMetadataResponse }, + + /// This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: + /// `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg` + #[returns(())] + GetCollectionInfoExtension { msg: TCollectionInfoExtension }, + + #[returns(Option)] + GetWithdrawAddress {}, +} + +/// Shows who can mint these tokens +#[cw_serde] +pub struct MinterResponse { + pub minter: Option, +} + +#[cw_serde] +pub enum Cw721MigrateMsg { + WithUpdate { + minter: Option, + creator: Option, + }, +} + +#[cw_serde] +pub struct CollectionInfoMsg { + pub name: String, + pub symbol: String, + pub extension: TCollectionInfoExtension, } diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index 155af3ea3..85deb27bc 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -1,81 +1,506 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::Empty; -use cw_utils::Expiration; +use cosmwasm_std::{ + to_json_binary, Addr, Binary, BlockInfo, CustomMsg, Deps, Empty, Env, Order, StdError, + StdResult, Storage, +}; +use cw_ownable::Ownership; +use cw_storage_plus::Bound; +use cw_utils::{maybe_addr, Expiration}; +use serde::de::DeserializeOwned; +use serde::Serialize; -use crate::CollectionInfo; +use crate::{ + msg::{Cw721QueryMsg, MinterResponse}, + state::{Approval, CollectionInfo, Cw721Config, NftInfo, CREATOR, MINTER}, +}; -#[cw_serde] -pub enum Cw721QueryMsg { - /// Return the owner of the given token, error if token does not exist - /// Return type: OwnerOfResponse - OwnerOf { - token_id: String, - /// unset or false will filter out expired approvals, you must set to true to see them - include_expired: Option, - }, - /// Return operator that can access all of the owner's tokens. - /// Return type: `ApprovalResponse` - Approval { +pub const DEFAULT_LIMIT: u32 = 10; +pub const MAX_LIMIT: u32 = 1000; + +pub trait Cw721Query +where + TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ + fn query( + &self, + deps: Deps, + env: Env, + msg: Cw721QueryMsg, + ) -> StdResult { + match msg { + #[allow(deprecated)] + Cw721QueryMsg::Minter {} => to_json_binary(&self.query_minter(deps.storage)?), + #[allow(deprecated)] + Cw721QueryMsg::ContractInfo {} => { + to_json_binary(&self.query_collection_info(deps, env)?) + } + Cw721QueryMsg::GetCollectionInfo {} => { + to_json_binary(&self.query_collection_info(deps, env)?) + } + Cw721QueryMsg::NftInfo { token_id } => { + to_json_binary(&self.query_nft_info(deps, env, token_id)?) + } + Cw721QueryMsg::OwnerOf { + token_id, + include_expired, + } => to_json_binary(&self.query_owner_of( + deps, + env, + token_id, + include_expired.unwrap_or(false), + )?), + Cw721QueryMsg::AllNftInfo { + token_id, + include_expired, + } => to_json_binary(&self.query_all_nft_info( + deps, + env, + token_id, + include_expired.unwrap_or(false), + )?), + Cw721QueryMsg::Operator { + owner, + operator, + include_expired, + } => to_json_binary(&self.query_operator( + deps, + env, + owner, + operator, + include_expired.unwrap_or(false), + )?), + Cw721QueryMsg::AllOperators { + owner, + include_expired, + start_after, + limit, + } => to_json_binary(&self.query_operators( + deps, + env, + owner, + include_expired.unwrap_or(false), + start_after, + limit, + )?), + Cw721QueryMsg::NumTokens {} => to_json_binary(&self.query_num_tokens(deps, env)?), + Cw721QueryMsg::Tokens { + owner, + start_after, + limit, + } => to_json_binary(&self.query_tokens(deps, env, owner, start_after, limit)?), + Cw721QueryMsg::AllTokens { start_after, limit } => { + to_json_binary(&self.query_all_tokens(deps, env, start_after, limit)?) + } + Cw721QueryMsg::Approval { + token_id, + spender, + include_expired, + } => to_json_binary(&self.query_approval( + deps, + env, + token_id, + spender, + include_expired.unwrap_or(false), + )?), + Cw721QueryMsg::Approvals { + token_id, + include_expired, + } => to_json_binary(&self.query_approvals( + deps, + env, + token_id, + include_expired.unwrap_or(false), + )?), + #[allow(deprecated)] + Cw721QueryMsg::Ownership {} => { + to_json_binary(&self.query_minter_ownership(deps.storage)?) + } + Cw721QueryMsg::GetMinterOwnership {} => { + to_json_binary(&self.query_minter_ownership(deps.storage)?) + } + Cw721QueryMsg::GetCreatorOwnership {} => { + to_json_binary(&self.query_creator_ownership(deps.storage)?) + } + Cw721QueryMsg::Extension { msg } => { + to_json_binary(&self.query_extension(deps, env, msg)?) + } + Cw721QueryMsg::GetCollectionInfoExtension { msg } => { + to_json_binary(&self.query_collection_info_extension(deps, env, msg)?) + } + Cw721QueryMsg::GetWithdrawAddress {} => { + to_json_binary(&self.query_withdraw_address(deps)?) + } + } + } + + #[deprecated(since = "0.19.0", note = "Please use minter_ownership instead")] + fn query_minter(&self, storage: &dyn Storage) -> StdResult { + let minter = MINTER + .get_ownership(storage)? + .owner + .map(|a| a.into_string()); + + Ok(MinterResponse { minter }) + } + + fn query_minter_ownership(&self, storage: &dyn Storage) -> StdResult> { + MINTER.get_ownership(storage) + } + + fn query_creator_ownership(&self, storage: &dyn Storage) -> StdResult> { + CREATOR.get_ownership(storage) + } + + fn query_collection_info( + &self, + deps: Deps, + _env: Env, + ) -> StdResult> { + Cw721Config::::default() + .collection_info + .load(deps.storage) + } + + fn query_num_tokens(&self, deps: Deps, _env: Env) -> StdResult { + let count = Cw721Config::< + TMetadata, + Empty, + Empty, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .token_count(deps.storage)?; + Ok(NumTokensResponse { count }) + } + + fn query_nft_info( + &self, + deps: Deps, + _env: Env, token_id: String, - spender: String, - include_expired: Option, - }, - /// Return approvals that a token has - /// Return type: `ApprovalsResponse` - Approvals { + ) -> StdResult> { + let info = Cw721Config::< + TMetadata, + Empty, + Empty, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .nft_info + .load(deps.storage, &token_id)?; + Ok(NftInfoResponse { + token_uri: info.token_uri, + extension: info.extension, + }) + } + + fn query_owner_of( + &self, + deps: Deps, + env: Env, token_id: String, - include_expired: Option, - }, - /// Return approval of a given operator for all tokens of an owner, error if not set - /// Return type: `OperatorResponse` - Operator { + include_expired_approval: bool, + ) -> StdResult { + let nft_info = Cw721Config::< + TMetadata, + Empty, + Empty, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .nft_info + .load(deps.storage, &token_id)?; + Ok(OwnerOfResponse { + owner: nft_info.owner.to_string(), + approvals: humanize_approvals(&env.block, &nft_info, include_expired_approval), + }) + } + + /// operator returns the approval status of an operator for a given owner if exists + fn query_operator( + &self, + deps: Deps, + env: Env, owner: String, operator: String, - include_expired: Option, - }, - /// List all operators that can access all of the owner's tokens - /// Return type: `OperatorsResponse` - AllOperators { + include_expired_approval: bool, + ) -> StdResult { + let owner_addr = deps.api.addr_validate(&owner)?; + let operator_addr = deps.api.addr_validate(&operator)?; + + let info = Cw721Config::< + TMetadata, + Empty, + Empty, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .operators + .may_load(deps.storage, (&owner_addr, &operator_addr))?; + + if let Some(expires) = info { + if !include_expired_approval && expires.is_expired(&env.block) { + return Err(StdError::not_found("Approval not found")); + } + + return Ok(OperatorResponse { + approval: Approval { + spender: operator_addr, + expires, + }, + }); + } + + Err(StdError::not_found("Approval not found")) + } + + /// operators returns all operators owner given access to + fn query_operators( + &self, + deps: Deps, + env: Env, owner: String, - /// unset or false will filter out expired items, you must set to true to see them - include_expired: Option, + include_expired_approval: bool, start_after: Option, limit: Option, - }, - /// Total number of tokens issued - NumTokens {}, - - /// With MetaData Extension. - /// Returns top-level metadata about the contract: `ContractInfoResponse` - ContractInfo {}, - /// With MetaData Extension. - /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* - /// but directly from the contract: `NftInfoResponse` - NftInfo { token_id: String }, - /// With MetaData Extension. - /// Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization - /// for clients: `AllNftInfo` - AllNftInfo { + ) -> StdResult { + 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.as_ref().map(Bound::exclusive); + + let owner_addr = deps.api.addr_validate(&owner)?; + let res: StdResult> = Cw721Config::< + TMetadata, + Empty, + Empty, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .operators + .prefix(&owner_addr) + .range(deps.storage, start, None, Order::Ascending) + .filter(|r| { + include_expired_approval || r.is_err() || !r.as_ref().unwrap().1.is_expired(&env.block) + }) + .take(limit) + .map(parse_approval) + .collect(); + Ok(OperatorsResponse { operators: res? }) + } + + fn query_approval( + &self, + deps: Deps, + env: Env, + token_id: String, + spender: String, + include_expired_approval: bool, + ) -> StdResult { + let token = Cw721Config::< + TMetadata, + Empty, + Empty, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .nft_info + .load(deps.storage, &token_id)?; + + // token owner has absolute approval + 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_approval || !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")); + } + // we expect only one item + let approval = filtered[0].clone(); + + Ok(ApprovalResponse { approval }) + } + + /// approvals returns all approvals owner given access to + fn query_approvals( + &self, + deps: Deps, + env: Env, token_id: String, - /// unset or false will filter out expired approvals, you must set to true to see them - include_expired: Option, - }, - - /// With Enumerable extension. - /// Returns all tokens owned by the given address, [] if unset. - /// Return type: TokensResponse. - Tokens { + include_expired_approval: bool, + ) -> StdResult { + let token = Cw721Config::< + TMetadata, + Empty, + Empty, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .nft_info + .load(deps.storage, &token_id)?; + let approvals: Vec<_> = token + .approvals + .into_iter() + .filter(|t| include_expired_approval || !t.is_expired(&env.block)) + .map(|a| Approval { + spender: a.spender, + expires: a.expires, + }) + .collect(); + + Ok(ApprovalsResponse { approvals }) + } + + fn query_tokens( + &self, + deps: Deps, + _env: Env, owner: String, start_after: Option, limit: Option, - }, - /// With Enumerable extension. - /// Requires pagination. Lists all token_ids controlled by the contract. - /// Return type: TokensResponse. - AllTokens { + ) -> StdResult { + let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; + let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); + + let owner_addr = deps.api.addr_validate(&owner)?; + let tokens: Vec = Cw721Config::< + TMetadata, + Empty, + Empty, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .nft_info + .idx + .owner + .prefix(owner_addr) + .keys(deps.storage, start, None, Order::Ascending) + .take(limit) + .collect::>>()?; + + Ok(TokensResponse { tokens }) + } + + fn query_all_tokens( + &self, + deps: Deps, + _env: Env, start_after: Option, limit: Option, - }, + ) -> StdResult { + let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; + let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); + + let tokens: StdResult> = Cw721Config::< + TMetadata, + Empty, + Empty, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .nft_info + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|item| item.map(|(k, _)| k)) + .collect(); + + Ok(TokensResponse { tokens: tokens? }) + } + + fn query_all_nft_info( + &self, + deps: Deps, + env: Env, + token_id: String, + include_expired_approval: bool, + ) -> StdResult> { + let nft_info = Cw721Config::< + TMetadata, + Empty, + Empty, + TMetadataResponse, + TCollectionInfoExtension, + >::default() + .nft_info + .load(deps.storage, &token_id)?; + Ok(AllNftInfoResponse { + access: OwnerOfResponse { + owner: nft_info.owner.to_string(), + approvals: humanize_approvals(&env.block, &nft_info, include_expired_approval), + }, + info: NftInfoResponse { + token_uri: nft_info.token_uri, + extension: nft_info.extension, + }, + }) + } + + fn query_extension( + &self, + _deps: Deps, + _env: Env, + _msg: TMetadataResponse, + ) -> StdResult { + Ok(Binary::default()) + } + + fn query_collection_info_extension( + &self, + _deps: Deps, + _env: Env, + _msg: TCollectionInfoExtension, + ) -> StdResult { + Ok(Binary::default()) + } + + fn query_withdraw_address(&self, deps: Deps) -> StdResult> { + Cw721Config::::default() + .withdraw_address + .may_load(deps.storage) + } +} + +pub fn parse_approval(item: StdResult<(Addr, Expiration)>) -> StdResult { + item.map(|(spender, expires)| Approval { spender, expires }) +} + +pub fn humanize_approvals( + block: &BlockInfo, + nft_info: &NftInfo, + include_expired_approval: bool, +) -> Vec +where + TMetadata: Serialize + DeserializeOwned + Clone, +{ + nft_info + .approvals + .iter() + .filter(|apr| include_expired_approval || !apr.is_expired(block)) + .map(humanize_approval) + .collect() +} + +pub fn humanize_approval(approval: &Approval) -> Approval { + Approval { + spender: approval.spender.clone(), + expires: approval.expires, + } } #[cw_serde] @@ -86,14 +511,6 @@ pub struct OwnerOfResponse { pub approvals: Vec, } -#[cw_serde] -pub struct Approval { - /// Account that can transfer/send the token - pub spender: String, - /// When the Approval expires (maybe Expiration::never) - pub expires: Expiration, -} - #[cw_serde] pub struct ApprovalResponse { pub approval: Approval, diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index 376548dbf..f824d3ff1 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -1,5 +1,198 @@ +use std::marker::PhantomData; + use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Decimal, Timestamp}; +use cosmwasm_std::{Addr, BlockInfo, CustomMsg, Decimal, Empty, StdResult, Storage, Timestamp}; +use cw_ownable::{OwnershipStore, OWNERSHIP_KEY}; +use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; +use cw_utils::Expiration; +use serde::de::DeserializeOwned; +use serde::Serialize; + +/// Creator owns this contract and can update collection info! +/// !!! Important note here: !!! +/// - creator is stored using using cw-ownable's OWNERSHIP singleton, so it is not stored here +/// - in release v0.18.0 it was used for minter (which is confusing), but now it is used for creator +pub const CREATOR: OwnershipStore = OwnershipStore::new(OWNERSHIP_KEY); +/// - minter is stored in the contract storage using cw_ownable::OwnershipStore (same as for OWNERSHIP but with different key) +pub const MINTER: OwnershipStore = OwnershipStore::new("collection_minter"); + +/// Default CollectionInfoExtension with RoyaltyInfo +pub type DefaultOptionCollectionInfoExtension = Option>; +pub type DefaultOptionMetadataExtension = Option; + +pub struct Cw721Config< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, +> where + TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataResponse: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ + /// Note: do not use deprecated/legacy key "nft_info"! + pub collection_info: Item<'a, CollectionInfo>, + pub token_count: Item<'a, u64>, + /// Stored as (granter, operator) giving operator full control over granter's account. + /// NOTE: granter is the owner, so operator has only control for NFTs owned by granter! + pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, + /// Note: do not use deprecated/legacy keys "tokens" and "tokens__owner"! + pub nft_info: IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, TMetadata>>, + pub withdraw_address: Item<'a, String>, + + pub(crate) _custom_response: PhantomData, + pub(crate) _custom_query: PhantomData, + pub(crate) _custom_execute: PhantomData, +} + +impl< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > Default + for Cw721Config< + 'static, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > +where + TMetadata: Serialize + DeserializeOwned + Clone, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ + fn default() -> Self { + Self::new( + "collection_info", // Note: do not use deprecated/legacy key "nft_info" + "num_tokens", + "operators", + "nft", // Note: do not use deprecated/legacy key "tokens" + "nft__owner", // Note: do not use deprecated/legacy key "tokens__owner" + "withdraw_address", + ) + } +} + +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > + Cw721Config< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > +where + TMetadata: Serialize + DeserializeOwned + Clone, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ + fn new( + collection_info_key: &'a str, + token_count_key: &'a str, + operator_key: &'a str, + nft_info_key: &'a str, + nft_info_owner_key: &'a str, + withdraw_address_key: &'a str, + ) -> Self { + let indexes = TokenIndexes { + owner: MultiIndex::new(token_owner_idx, nft_info_key, nft_info_owner_key), + }; + Self { + collection_info: Item::new(collection_info_key), + token_count: Item::new(token_count_key), + operators: Map::new(operator_key), + nft_info: IndexedMap::new(nft_info_key, indexes), + withdraw_address: Item::new(withdraw_address_key), + _custom_response: PhantomData, + _custom_execute: PhantomData, + _custom_query: PhantomData, + } + } + + pub fn token_count(&self, storage: &dyn Storage) -> StdResult { + Ok(self.token_count.may_load(storage)?.unwrap_or_default()) + } + + pub fn increment_tokens(&self, storage: &mut dyn Storage) -> StdResult { + let val = self.token_count(storage)? + 1; + self.token_count.save(storage, &val)?; + Ok(val) + } + + pub fn decrement_tokens(&self, storage: &mut dyn Storage) -> StdResult { + let val = self.token_count(storage)? - 1; + self.token_count.save(storage, &val)?; + Ok(val) + } +} + +pub fn token_owner_idx(_pk: &[u8], d: &NftInfo) -> Addr { + d.owner.clone() +} + +#[cw_serde] +pub struct NftInfo { + /// The owner of the newly minted NFT + pub owner: Addr, + /// Approvals are stored here, as we clear them all upon transfer and cannot accumulate much + pub approvals: Vec, + + /// Universal resource identifier for this NFT + /// Should point to a JSON file that conforms to the ERC721 + /// Metadata JSON Schema + pub token_uri: Option, + + /// You can add any custom metadata here when you extend cw721-base + pub extension: TMetadata, +} + +#[cw_serde] +pub struct Approval { + /// Account that can transfer/send the token + pub spender: Addr, + /// When the Approval expires (maybe Expiration::never) + pub expires: Expiration, +} + +impl Approval { + pub fn is_expired(&self, block: &BlockInfo) -> bool { + self.expires.is_expired(block) + } +} + +pub struct TokenIndexes<'a, TMetadata> +where + TMetadata: Serialize + DeserializeOwned + Clone, +{ + pub owner: MultiIndex<'a, Addr, NftInfo, String>, +} + +impl<'a, TMetadata> IndexList> for TokenIndexes<'a, TMetadata> +where + TMetadata: Serialize + DeserializeOwned + Clone, +{ + fn get_indexes(&'_ self) -> Box>> + '_> { + let v: Vec<&dyn Index>> = vec![&self.owner]; + Box::new(v.into_iter()) + } +} #[cw_serde] pub struct CollectionInfo { @@ -11,10 +204,11 @@ pub struct CollectionInfo { #[cw_serde] pub struct CollectionInfoExtension { - pub description: Option, - pub image: Option, + pub description: String, + pub image: String, pub external_link: Option, pub explicit_content: Option, + pub start_trading_time: Option, pub royalty_info: Option, } @@ -45,5 +239,3 @@ pub struct Trait { pub trait_type: String, pub value: String, } - -pub type MetadataExtension = Option; diff --git a/packages/cw721/src/testing/contract.rs b/packages/cw721/src/testing/contract.rs new file mode 100644 index 000000000..564662d12 --- /dev/null +++ b/packages/cw721/src/testing/contract.rs @@ -0,0 +1,115 @@ +use cosmwasm_std::CustomMsg; +use serde::de::DeserializeOwned; +use serde::Serialize; + +use crate::execute::Cw721Execute; +use crate::query::Cw721Query; +use crate::state::Cw721Config; + +pub struct Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, +> where + TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataResponse: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ + pub config: Cw721Config< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + >, +} + +impl< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > Default + for Cw721Contract< + 'static, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > +where + TMetadata: Serialize + DeserializeOwned + Clone, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ + fn default() -> Self { + Self { + config: Cw721Config::default(), + } + } +} + +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > + Cw721Execute< + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > + for Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > +where + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ +} + +impl< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > Cw721Query + for Cw721Contract< + 'a, + TMetadata, + TCustomResponseMessage, + TExtensionExecuteMsg, + TMetadataResponse, + TCollectionInfoExtension, + > +where + TMetadata: Serialize + DeserializeOwned + Clone, + TCustomResponseMessage: CustomMsg, + TExtensionExecuteMsg: CustomMsg, + TMetadataResponse: CustomMsg, + TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, +{ +} diff --git a/contracts/cw721-base/src/contract_tests.rs b/packages/cw721/src/testing/contract_tests.rs similarity index 64% rename from contracts/cw721-base/src/contract_tests.rs rename to packages/cw721/src/testing/contract_tests.rs index 98729f490..9382b83fa 100644 --- a/contracts/cw721-base/src/contract_tests.rs +++ b/packages/cw721/src/testing/contract_tests.rs @@ -2,17 +2,25 @@ use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{ - from_json, to_json_binary, Addr, Coin, CosmosMsg, DepsMut, Empty, Response, StdError, WasmMsg, + from_json, to_json_binary, Addr, Coin, CosmosMsg, CustomMsg, DepsMut, Empty, Response, + StdError, Timestamp, WasmMsg, }; -use cw721::{ - Approval, ApprovalResponse, CollectionInfo, Cw721Query, Cw721ReceiveMsg, Expiration, - NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, +use crate::error::Cw721ContractError; +use crate::msg::{CollectionInfoMsg, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721QueryMsg}; +use crate::query::{ + ApprovalResponse, NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, }; +use crate::receiver::Cw721ReceiveMsg; +use crate::state::{ + CollectionInfo, Cw721Config, DefaultOptionCollectionInfoExtension, + DefaultOptionMetadataExtension, CREATOR, MINTER, +}; +use crate::{execute::Cw721Execute, query::Cw721Query, Approval, Expiration}; +use crate::{CollectionInfoExtension, RoyaltyInfo}; use cw_ownable::{Action, Ownership, OwnershipError}; -use crate::state::{CREATOR, MINTER}; -use crate::{ContractError, Cw721Contract, EmptyExtension, ExecuteMsg, InstantiateMsg, QueryMsg}; +use super::contract::Cw721Contract; const MINTER_ADDR: &str = "minter"; const CREATOR_ADDR: &str = "creator"; @@ -21,9 +29,9 @@ const SYMBOL: &str = "MGK"; fn setup_contract( deps: DepsMut<'_>, -) -> Cw721Contract<'static, EmptyExtension, Empty, Empty, Empty, Empty> { +) -> Cw721Contract<'static, DefaultOptionMetadataExtension, Empty, Empty, Empty, Empty> { let contract = Cw721Contract::default(); - let msg = InstantiateMsg { + let msg = Cw721InstantiateMsg { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), collection_info_extension: Empty {}, @@ -32,7 +40,16 @@ fn setup_contract( withdraw_address: None, }; let info = mock_info("creator", &[]); - let res = contract.instantiate(deps, mock_env(), info, msg).unwrap(); + let res = contract + .instantiate( + deps, + mock_env(), + info, + msg, + "contract_name", + "contract_version", + ) + .unwrap(); assert_eq!(0, res.messages.len()); contract } @@ -40,9 +57,10 @@ fn setup_contract( #[test] fn proper_instantiation() { let mut deps = mock_dependencies(); - let contract = Cw721Contract::::default(); + let contract = + Cw721Contract::::default(); - let msg = InstantiateMsg { + let msg = Cw721InstantiateMsg { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), collection_info_extension: Empty {}, @@ -55,7 +73,14 @@ fn proper_instantiation() { // we can just call .unwrap() to assert this was a success let res = contract - .instantiate(deps.as_mut(), env.clone(), info, msg) + .instantiate( + deps.as_mut(), + env.clone(), + info, + msg, + "contract_name", + "contract_version", + ) .unwrap(); assert_eq!(0, res.messages.len()); @@ -64,9 +89,11 @@ fn proper_instantiation() { assert_eq!(Some(Addr::unchecked(MINTER_ADDR)), minter_ownership.owner); let creator_ownership = CREATOR.get_ownership(deps.as_ref().storage).unwrap(); assert_eq!(Some(Addr::unchecked(CREATOR_ADDR)), creator_ownership.owner); - let info = contract.collection_info(deps.as_ref()).unwrap(); + let collection_info = contract + .query_collection_info(deps.as_ref(), env.clone()) + .unwrap(); assert_eq!( - info, + collection_info, CollectionInfo { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), @@ -76,16 +103,104 @@ fn proper_instantiation() { ); let withdraw_address = contract + .config + .withdraw_address + .may_load(deps.as_ref().storage) + .unwrap(); + assert_eq!(Some(CREATOR_ADDR.to_string()), withdraw_address); + + let count = contract + .query_num_tokens(deps.as_ref(), env.clone()) + .unwrap(); + assert_eq!(0, count.count); + + // list the token_ids + let tokens = contract + .query_all_tokens(deps.as_ref(), env, None, None) + .unwrap(); + assert_eq!(0, tokens.tokens.len()); +} + +#[test] +fn proper_instantiation_with_collection_info() { + let mut deps = mock_dependencies(); + let contract = Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + + let collection_info_extension = Some(CollectionInfoExtension { + description: "description".to_string(), + image: "image".to_string(), + explicit_content: Some(true), + external_link: Some("external_link".to_string()), + start_trading_time: Some(Timestamp::from_seconds(0)), + royalty_info: Some(RoyaltyInfo { + payment_address: Addr::unchecked("payment_address"), + share: "0.1".parse().unwrap(), + }), + }); + let msg = Cw721InstantiateMsg:: { + name: CONTRACT_NAME.to_string(), + symbol: SYMBOL.to_string(), + collection_info_extension: collection_info_extension.clone(), + minter: Some(String::from(MINTER_ADDR)), + creator: Some(String::from(CREATOR_ADDR)), + withdraw_address: Some(String::from(CREATOR_ADDR)), + }; + let collection_info = mock_info("creator", &[]); + let env = mock_env(); + + // we can just call .unwrap() to assert this was a success + let res = contract + .instantiate( + deps.as_mut(), + env.clone(), + collection_info, + msg, + "contract_name", + "contract_version", + ) + .unwrap(); + assert_eq!(0, res.messages.len()); + + // it worked, let's query the state + let minter_ownership = MINTER.get_ownership(deps.as_ref().storage).unwrap(); + assert_eq!(Some(Addr::unchecked(MINTER_ADDR)), minter_ownership.owner); + let creator_ownership = CREATOR.get_ownership(deps.as_ref().storage).unwrap(); + assert_eq!(Some(Addr::unchecked(CREATOR_ADDR)), creator_ownership.owner); + let info = contract + .query_collection_info(deps.as_ref(), env.clone()) + .unwrap(); + assert_eq!( + info, + CollectionInfo { + name: CONTRACT_NAME.to_string(), + symbol: SYMBOL.to_string(), + extension: collection_info_extension, + updated_at: env.block.time + } + ); + + let withdraw_address = contract + .config .withdraw_address .may_load(deps.as_ref().storage) .unwrap(); assert_eq!(Some(CREATOR_ADDR.to_string()), withdraw_address); - let count = contract.num_tokens(deps.as_ref()).unwrap(); + let count = contract + .query_num_tokens(deps.as_ref(), env.clone()) + .unwrap(); assert_eq!(0, count.count); // list the token_ids - let tokens = contract.all_tokens(deps.as_ref(), None, None).unwrap(); + let tokens = contract + .query_all_tokens(deps.as_ref(), env, None, None) + .unwrap(); assert_eq!(0, tokens.tokens.len()); } @@ -97,7 +212,7 @@ fn minting() { let token_id = "petrify".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/petrify".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: String::from("medusa"), token_uri: Some(token_uri.clone()), @@ -106,31 +221,36 @@ fn minting() { // random cannot mint let random = mock_info("random", &[]); + let env = mock_env(); let err = contract - .execute(deps.as_mut(), mock_env(), random, mint_msg.clone()) + .execute(deps.as_mut(), env.clone(), random, mint_msg.clone()) .unwrap_err(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // minter can mint let allowed = mock_info(MINTER_ADDR, &[]); let _ = contract - .execute(deps.as_mut(), mock_env(), allowed, mint_msg) + .execute(deps.as_mut(), env.clone(), allowed, mint_msg) .unwrap(); // ensure num tokens increases - let count = contract.num_tokens(deps.as_ref()).unwrap(); + let count = contract + .query_num_tokens(deps.as_ref(), env.clone()) + .unwrap(); assert_eq!(1, count.count); // unknown nft returns error let _ = contract - .nft_info(deps.as_ref(), "unknown".to_string()) + .query_nft_info(deps.as_ref(), env.clone(), "unknown".to_string()) .unwrap_err(); // this nft info is correct - let info = contract.nft_info(deps.as_ref(), token_id.clone()).unwrap(); + let info = contract + .query_nft_info(deps.as_ref(), env.clone(), token_id.clone()) + .unwrap(); assert_eq!( info, - NftInfoResponse:: { + NftInfoResponse:: { token_uri: Some(token_uri), extension: None, } @@ -138,7 +258,7 @@ fn minting() { // owner info is correct let owner = contract - .owner_of(deps.as_ref(), mock_env(), token_id.clone(), true) + .query_owner_of(deps.as_ref(), mock_env(), token_id.clone(), true) .unwrap(); assert_eq!( owner, @@ -149,7 +269,7 @@ fn minting() { ); // Cannot mint same token_id again - let mint_msg2 = ExecuteMsg::Mint { + let mint_msg2 = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: String::from("hercules"), token_uri: None, @@ -160,14 +280,130 @@ fn minting() { let err = contract .execute(deps.as_mut(), mock_env(), allowed, mint_msg2) .unwrap_err(); - assert_eq!(err, ContractError::Claimed {}); + assert_eq!(err, Cw721ContractError::Claimed {}); // list the token_ids - let tokens = contract.all_tokens(deps.as_ref(), None, None).unwrap(); + let tokens = contract + .query_all_tokens(deps.as_ref(), env, None, None) + .unwrap(); assert_eq!(1, tokens.tokens.len()); assert_eq!(vec![token_id], tokens.tokens); } +#[test] +fn test_update_collection_info() { + let mut deps = mock_dependencies(); + let contract = setup_contract(deps.as_mut()); + + let update_collection_info_msg = Cw721ExecuteMsg::UpdateCollectionInfo { + collection_info: CollectionInfoMsg { + name: "new name".to_string(), + symbol: "NEW".to_string(), + extension: Empty {}, + }, + }; + + // Creator can update collection info + let creator_info = mock_info(CREATOR_ADDR, &[]); + let _ = contract + .execute( + deps.as_mut(), + mock_env(), + creator_info.clone(), + update_collection_info_msg, + ) + .unwrap(); + + // Update the owner to "random". The new owner should be able to + // mint new tokens, the old one should not. + contract + .execute( + deps.as_mut(), + mock_env(), + creator_info.clone(), + Cw721ExecuteMsg::UpdateCreatorOwnership(Action::TransferOwnership { + new_owner: "random".to_string(), + expiry: None, + }), + ) + .unwrap(); + + // Creator does not change until ownership transfer completes. + // Pending ownership transfer should be discoverable via query. + let ownership: Ownership = from_json( + contract + .query( + deps.as_ref(), + mock_env(), + Cw721QueryMsg::GetCreatorOwnership {}, + ) + .unwrap(), + ) + .unwrap(); + + assert_eq!( + ownership, + Ownership:: { + owner: Some(Addr::unchecked(CREATOR_ADDR)), + pending_owner: Some(Addr::unchecked("random")), + pending_expiry: None, + } + ); + + // Accept the ownership transfer. + let random_info = mock_info("random", &[]); + contract + .execute( + deps.as_mut(), + mock_env(), + random_info.clone(), + Cw721ExecuteMsg::UpdateCreatorOwnership(Action::AcceptOwnership), + ) + .unwrap(); + + // Creator changes after ownership transfer is accepted. + let creator_ownership: Ownership = from_json( + contract + .query( + deps.as_ref(), + mock_env(), + Cw721QueryMsg::GetCreatorOwnership {}, + ) + .unwrap(), + ) + .unwrap(); + assert_eq!(creator_ownership.owner, Some(random_info.sender.clone())); + + let update_collection_info_msg = Cw721ExecuteMsg::UpdateCollectionInfo { + collection_info: CollectionInfoMsg { + name: "new name".to_string(), + symbol: "NEW".to_string(), + extension: Empty {}, + }, + }; + + // Old owner can not update. + let err: Cw721ContractError = contract + .execute( + deps.as_mut(), + mock_env(), + creator_info, + update_collection_info_msg.clone(), + ) + .unwrap_err(); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); + + // New owner can update. + let _ = contract + .execute( + deps.as_mut(), + mock_env(), + random_info, + update_collection_info_msg, + ) + .unwrap(); +} + #[test] fn test_update_minter() { let mut deps = mock_dependencies(); @@ -176,7 +412,7 @@ fn test_update_minter() { let token_id = "petrify".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/petrify".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id, owner: String::from("medusa"), token_uri: Some(token_uri.clone()), @@ -196,7 +432,7 @@ fn test_update_minter() { deps.as_mut(), mock_env(), minter_info.clone(), - ExecuteMsg::UpdateMinterOwnership(Action::TransferOwnership { + Cw721ExecuteMsg::UpdateMinterOwnership(Action::TransferOwnership { new_owner: "random".to_string(), expiry: None, }), @@ -207,7 +443,11 @@ fn test_update_minter() { // Pending ownership transfer should be discoverable via query. let ownership: Ownership = from_json( contract - .query(deps.as_ref(), mock_env(), QueryMsg::GetMinterOwnership {}) + .query( + deps.as_ref(), + mock_env(), + Cw721QueryMsg::GetMinterOwnership {}, + ) .unwrap(), ) .unwrap(); @@ -228,20 +468,24 @@ fn test_update_minter() { deps.as_mut(), mock_env(), random_info.clone(), - ExecuteMsg::UpdateMinterOwnership(Action::AcceptOwnership), + Cw721ExecuteMsg::UpdateMinterOwnership(Action::AcceptOwnership), ) .unwrap(); // Minter changes after ownership transfer is accepted. let minter_ownership: Ownership = from_json( contract - .query(deps.as_ref(), mock_env(), QueryMsg::GetMinterOwnership {}) + .query( + deps.as_ref(), + mock_env(), + Cw721QueryMsg::GetMinterOwnership {}, + ) .unwrap(), ) .unwrap(); assert_eq!(minter_ownership.owner, Some(random_info.sender.clone())); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: "randoms_token".to_string(), owner: String::from("medusa"), token_uri: Some(token_uri), @@ -249,10 +493,10 @@ fn test_update_minter() { }; // Old owner can not mint. - let err: ContractError = contract + let err: Cw721ContractError = contract .execute(deps.as_mut(), mock_env(), minter_info, mint_msg.clone()) .unwrap_err(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // New owner can mint. let _ = contract @@ -268,14 +512,14 @@ fn burning() { let token_id = "petrify".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/petrify".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: MINTER_ADDR.to_string(), token_uri: Some(token_uri), extension: None, }; - let burn_msg = ExecuteMsg::Burn { token_id }; + let burn_msg = Cw721ExecuteMsg::Burn { token_id }; // mint some NFT let allowed = mock_info(MINTER_ADDR, &[]); @@ -285,27 +529,32 @@ fn burning() { // random not allowed to burn let random = mock_info("random", &[]); + let env = mock_env(); let err = contract - .execute(deps.as_mut(), mock_env(), random, burn_msg.clone()) + .execute(deps.as_mut(), env.clone(), random, burn_msg.clone()) .unwrap_err(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); let _ = contract - .execute(deps.as_mut(), mock_env(), allowed, burn_msg) + .execute(deps.as_mut(), env.clone(), allowed, burn_msg) .unwrap(); // ensure num tokens decreases - let count = contract.num_tokens(deps.as_ref()).unwrap(); + let count = contract + .query_num_tokens(deps.as_ref(), env.clone()) + .unwrap(); assert_eq!(0, count.count); // trying to get nft returns error let _ = contract - .nft_info(deps.as_ref(), "petrify".to_string()) + .query_nft_info(deps.as_ref(), env.clone(), "petrify".to_string()) .unwrap_err(); // list the token_ids - let tokens = contract.all_tokens(deps.as_ref(), None, None).unwrap(); + let tokens = contract + .query_all_tokens(deps.as_ref(), env, None, None) + .unwrap(); assert!(tokens.tokens.is_empty()); } @@ -318,7 +567,7 @@ fn transferring_nft() { let token_id = "melt".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/melt".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: String::from("venus"), token_uri: Some(token_uri), @@ -332,7 +581,7 @@ fn transferring_nft() { // random cannot transfer let random = mock_info("random", &[]); - let transfer_msg = ExecuteMsg::TransferNft { + let transfer_msg = Cw721ExecuteMsg::TransferNft { recipient: String::from("random"), token_id: token_id.clone(), }; @@ -340,11 +589,11 @@ fn transferring_nft() { let err = contract .execute(deps.as_mut(), mock_env(), random, transfer_msg) .unwrap_err(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // owner can let random = mock_info("venus", &[]); - let transfer_msg = ExecuteMsg::TransferNft { + let transfer_msg = Cw721ExecuteMsg::TransferNft { recipient: String::from("random"), token_id: token_id.clone(), }; @@ -372,7 +621,7 @@ fn sending_nft() { let token_id = "melt".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/melt".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: String::from("venus"), token_uri: Some(token_uri), @@ -386,7 +635,7 @@ fn sending_nft() { let msg = to_json_binary("You now have the melting power").unwrap(); let target = String::from("another_contract"); - let send_msg = ExecuteMsg::SendNft { + let send_msg = Cw721ExecuteMsg::SendNft { contract: target.clone(), token_id: token_id.clone(), msg: msg.clone(), @@ -396,7 +645,7 @@ fn sending_nft() { let err = contract .execute(deps.as_mut(), mock_env(), random, send_msg.clone()) .unwrap_err(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // but owner can let random = mock_info("venus", &[]); @@ -438,7 +687,7 @@ fn approving_revoking() { let token_id = "grow".to_string(); let token_uri = "https://www.merriam-webster.com/dictionary/grow".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id.clone(), owner: String::from("demeter"), token_uri: Some(token_uri), @@ -452,7 +701,7 @@ fn approving_revoking() { // token owner shows in approval query let res = contract - .approval( + .query_approval( deps.as_ref(), mock_env(), token_id.clone(), @@ -464,14 +713,14 @@ fn approving_revoking() { res, ApprovalResponse { approval: Approval { - spender: String::from("demeter"), + spender: Addr::unchecked("demeter"), expires: Expiration::Never {} } } ); // Give random transferring power - let approve_msg = ExecuteMsg::Approve { + let approve_msg = Cw721ExecuteMsg::Approve { spender: String::from("random"), token_id: token_id.clone(), expires: None, @@ -491,7 +740,7 @@ fn approving_revoking() { // test approval query let res = contract - .approval( + .query_approval( deps.as_ref(), mock_env(), token_id.clone(), @@ -503,7 +752,7 @@ fn approving_revoking() { res, ApprovalResponse { approval: Approval { - spender: String::from("random"), + spender: Addr::unchecked("random"), expires: Expiration::Never {} } } @@ -511,7 +760,7 @@ fn approving_revoking() { // random can now transfer let random = mock_info("random", &[]); - let transfer_msg = ExecuteMsg::TransferNft { + let transfer_msg = Cw721ExecuteMsg::TransferNft { recipient: String::from("person"), token_id: token_id.clone(), }; @@ -520,7 +769,7 @@ fn approving_revoking() { .unwrap(); // Approvals are removed / cleared - let query_msg = QueryMsg::OwnerOf { + let query_msg = Cw721QueryMsg::OwnerOf { token_id: token_id.clone(), include_expired: None, }; @@ -539,7 +788,7 @@ fn approving_revoking() { ); // Approve, revoke, and check for empty, to test revoke - let approve_msg = ExecuteMsg::Approve { + let approve_msg = Cw721ExecuteMsg::Approve { spender: String::from("random"), token_id: token_id.clone(), expires: None, @@ -549,7 +798,7 @@ fn approving_revoking() { .execute(deps.as_mut(), mock_env(), owner.clone(), approve_msg) .unwrap(); - let revoke_msg = ExecuteMsg::Revoke { + let revoke_msg = Cw721ExecuteMsg::Revoke { spender: String::from("random"), token_id, }; @@ -585,7 +834,7 @@ fn approving_all_revoking_all() { let token_id2 = "grow2".to_string(); let token_uri2 = "https://www.merriam-webster.com/dictionary/grow2".to_string(); - let mint_msg1 = ExecuteMsg::Mint { + let mint_msg1 = Cw721ExecuteMsg::Mint { token_id: token_id1.clone(), owner: String::from("demeter"), token_uri: Some(token_uri1), @@ -597,29 +846,32 @@ fn approving_all_revoking_all() { .execute(deps.as_mut(), mock_env(), minter.clone(), mint_msg1) .unwrap(); - let mint_msg2 = ExecuteMsg::Mint { + let mint_msg2 = Cw721ExecuteMsg::Mint { token_id: token_id2.clone(), owner: String::from("demeter"), token_uri: Some(token_uri2), extension: None, }; + let env = mock_env(); contract - .execute(deps.as_mut(), mock_env(), minter, mint_msg2) + .execute(deps.as_mut(), env.clone(), minter, mint_msg2) .unwrap(); // paginate the token_ids - let tokens = contract.all_tokens(deps.as_ref(), None, Some(1)).unwrap(); + let tokens = contract + .query_all_tokens(deps.as_ref(), env.clone(), None, Some(1)) + .unwrap(); assert_eq!(1, tokens.tokens.len()); assert_eq!(vec![token_id1.clone()], tokens.tokens); let tokens = contract - .all_tokens(deps.as_ref(), Some(token_id1.clone()), Some(3)) + .query_all_tokens(deps.as_ref(), env, Some(token_id1.clone()), Some(3)) .unwrap(); assert_eq!(1, tokens.tokens.len()); assert_eq!(vec![token_id2.clone()], tokens.tokens); // demeter gives random full (operator) power over her tokens - let approve_all_msg = ExecuteMsg::ApproveAll { + let approve_all_msg = Cw721ExecuteMsg::ApproveAll { operator: String::from("random"), expires: None, }; @@ -637,7 +889,7 @@ fn approving_all_revoking_all() { // random can now transfer let random = mock_info("random", &[]); - let transfer_msg = ExecuteMsg::TransferNft { + let transfer_msg = Cw721ExecuteMsg::TransferNft { recipient: String::from("person"), token_id: token_id1, }; @@ -653,7 +905,7 @@ fn approving_all_revoking_all() { }; let msg: CosmosMsg = CosmosMsg::Wasm(inner_msg); - let send_msg = ExecuteMsg::SendNft { + let send_msg = Cw721ExecuteMsg::SendNft { contract: String::from("another_contract"), token_id: token_id2, msg: to_json_binary(&msg).unwrap(), @@ -663,7 +915,7 @@ fn approving_all_revoking_all() { .unwrap(); // Approve_all, revoke_all, and check for empty, to test revoke_all - let approve_all_msg = ExecuteMsg::ApproveAll { + let approve_all_msg = Cw721ExecuteMsg::ApproveAll { operator: String::from("operator"), expires: None, }; @@ -675,7 +927,7 @@ fn approving_all_revoking_all() { // query for operator should return approval let res = contract - .operator( + .query_operator( deps.as_ref(), mock_env(), String::from("person"), @@ -687,14 +939,14 @@ fn approving_all_revoking_all() { res, OperatorResponse { approval: Approval { - spender: String::from("operator"), + spender: Addr::unchecked("operator"), expires: Expiration::Never {} } } ); // query for other should throw error - let res = contract.operator( + let res = contract.query_operator( deps.as_ref(), mock_env(), String::from("person"), @@ -707,7 +959,7 @@ fn approving_all_revoking_all() { } let res = contract - .operators( + .query_operators( deps.as_ref(), mock_env(), String::from("person"), @@ -719,8 +971,8 @@ fn approving_all_revoking_all() { assert_eq!( res, OperatorsResponse { - operators: vec![cw721::Approval { - spender: String::from("operator"), + operators: vec![Approval { + spender: Addr::unchecked("operator"), expires: Expiration::Never {} }] } @@ -728,7 +980,7 @@ fn approving_all_revoking_all() { // second approval let buddy_expires = Expiration::AtHeight(1234567); - let approve_all_msg = ExecuteMsg::ApproveAll { + let approve_all_msg = Cw721ExecuteMsg::ApproveAll { operator: String::from("buddy"), expires: Some(buddy_expires), }; @@ -739,7 +991,7 @@ fn approving_all_revoking_all() { // and paginate queries let res = contract - .operators( + .query_operators( deps.as_ref(), mock_env(), String::from("person"), @@ -751,14 +1003,14 @@ fn approving_all_revoking_all() { assert_eq!( res, OperatorsResponse { - operators: vec![cw721::Approval { - spender: String::from("buddy"), + operators: vec![Approval { + spender: Addr::unchecked("buddy"), expires: buddy_expires, }] } ); let res = contract - .operators( + .query_operators( deps.as_ref(), mock_env(), String::from("person"), @@ -770,14 +1022,14 @@ fn approving_all_revoking_all() { assert_eq!( res, OperatorsResponse { - operators: vec![cw721::Approval { - spender: String::from("operator"), + operators: vec![Approval { + spender: Addr::unchecked("operator"), expires: Expiration::Never {} }] } ); - let revoke_all_msg = ExecuteMsg::RevokeAll { + let revoke_all_msg = Cw721ExecuteMsg::RevokeAll { operator: String::from("operator"), }; contract @@ -785,7 +1037,7 @@ fn approving_all_revoking_all() { .unwrap(); // query for operator should return error - let res = contract.operator( + let res = contract.query_operator( deps.as_ref(), mock_env(), String::from("person"), @@ -799,7 +1051,7 @@ fn approving_all_revoking_all() { // Approvals are removed / cleared without affecting others let res = contract - .operators( + .query_operators( deps.as_ref(), mock_env(), String::from("person"), @@ -811,8 +1063,8 @@ fn approving_all_revoking_all() { assert_eq!( res, OperatorsResponse { - operators: vec![cw721::Approval { - spender: String::from("buddy"), + operators: vec![Approval { + spender: Addr::unchecked("buddy"), expires: buddy_expires, }] } @@ -822,7 +1074,7 @@ fn approving_all_revoking_all() { let mut late_env = mock_env(); late_env.block.height = 1234568; //expired let res = contract - .operators( + .query_operators( deps.as_ref(), late_env.clone(), String::from("person"), @@ -834,7 +1086,7 @@ fn approving_all_revoking_all() { assert_eq!(0, res.operators.len()); // query operator should also return error - let res = contract.operator( + let res = contract.query_operator( deps.as_ref(), late_env, String::from("person"), @@ -861,7 +1113,7 @@ fn test_set_withdraw_address() { "foo".to_string(), ) .unwrap_err(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // creator can set contract @@ -873,6 +1125,7 @@ fn test_set_withdraw_address() { .unwrap(); let withdraw_address = contract + .config .withdraw_address .load(deps.as_ref().storage) .unwrap(); @@ -888,13 +1141,13 @@ fn test_remove_withdraw_address() { let err = contract .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(MINTER_ADDR)) .unwrap_err(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // no withdraw address set yet let err = contract .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(CREATOR_ADDR)) .unwrap_err(); - assert_eq!(err, ContractError::NoWithdrawAddress {}); + assert_eq!(err, Cw721ContractError::NoWithdrawAddress {}); // set and remove contract @@ -907,7 +1160,10 @@ fn test_remove_withdraw_address() { contract .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(CREATOR_ADDR)) .unwrap(); - assert!(!contract.withdraw_address.exists(deps.as_ref().storage)); + assert!(!contract + .config + .withdraw_address + .exists(deps.as_ref().storage)); // test that we can set again contract @@ -918,6 +1174,7 @@ fn test_remove_withdraw_address() { ) .unwrap(); let withdraw_address = contract + .config .withdraw_address .load(deps.as_ref().storage) .unwrap(); @@ -933,7 +1190,7 @@ fn test_withdraw_funds() { let err = contract .withdraw_funds(deps.as_mut().storage, &Coin::new(100, "uark")) .unwrap_err(); - assert_eq!(err, ContractError::NoWithdrawAddress {}); + assert_eq!(err, Cw721ContractError::NoWithdrawAddress {}); // set and withdraw by non-creator contract @@ -961,7 +1218,7 @@ fn query_tokens_by_owner() { let ceres = String::from("ceres"); let token_id3 = "sing".to_string(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id1.clone(), owner: demeter.clone(), token_uri: None, @@ -971,7 +1228,7 @@ fn query_tokens_by_owner() { .execute(deps.as_mut(), mock_env(), minter.clone(), mint_msg) .unwrap(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id2.clone(), owner: ceres.clone(), token_uri: None, @@ -981,25 +1238,30 @@ fn query_tokens_by_owner() { .execute(deps.as_mut(), mock_env(), minter.clone(), mint_msg) .unwrap(); - let mint_msg = ExecuteMsg::Mint { + let mint_msg = Cw721ExecuteMsg::Mint { token_id: token_id3.clone(), owner: demeter.clone(), token_uri: None, extension: None, }; + let env = mock_env(); contract - .execute(deps.as_mut(), mock_env(), minter, mint_msg) + .execute(deps.as_mut(), env.clone(), minter, mint_msg) .unwrap(); // get all tokens in order: let expected = vec![token_id1.clone(), token_id2.clone(), token_id3.clone()]; - let tokens = contract.all_tokens(deps.as_ref(), None, None).unwrap(); + let tokens = contract + .query_all_tokens(deps.as_ref(), env.clone(), None, None) + .unwrap(); assert_eq!(&expected, &tokens.tokens); // paginate - let tokens = contract.all_tokens(deps.as_ref(), None, Some(2)).unwrap(); + let tokens = contract + .query_all_tokens(deps.as_ref(), env.clone(), None, Some(2)) + .unwrap(); assert_eq!(&expected[..2], &tokens.tokens[..]); let tokens = contract - .all_tokens(deps.as_ref(), Some(expected[1].clone()), None) + .query_all_tokens(deps.as_ref(), env.clone(), Some(expected[1].clone()), None) .unwrap(); assert_eq!(&expected[2..], &tokens.tokens[..]); @@ -1008,19 +1270,27 @@ fn query_tokens_by_owner() { let by_demeter = vec![token_id1, token_id3]; // all tokens by owner let tokens = contract - .tokens(deps.as_ref(), demeter.clone(), None, None) + .query_tokens(deps.as_ref(), env.clone(), demeter.clone(), None, None) .unwrap(); assert_eq!(&by_demeter, &tokens.tokens); - let tokens = contract.tokens(deps.as_ref(), ceres, None, None).unwrap(); + let tokens = contract + .query_tokens(deps.as_ref(), env.clone(), ceres, None, None) + .unwrap(); assert_eq!(&by_ceres, &tokens.tokens); // paginate for demeter let tokens = contract - .tokens(deps.as_ref(), demeter.clone(), None, Some(1)) + .query_tokens(deps.as_ref(), env.clone(), demeter.clone(), None, Some(1)) .unwrap(); assert_eq!(&by_demeter[..1], &tokens.tokens[..]); let tokens = contract - .tokens(deps.as_ref(), demeter, Some(by_demeter[0].clone()), Some(3)) + .query_tokens( + deps.as_ref(), + env, + demeter, + Some(by_demeter[0].clone()), + Some(3), + ) .unwrap(); assert_eq!(&by_demeter[1..], &tokens.tokens[..]); } diff --git a/packages/cw721/src/testing/mod.rs b/packages/cw721/src/testing/mod.rs new file mode 100644 index 000000000..b94a351ae --- /dev/null +++ b/packages/cw721/src/testing/mod.rs @@ -0,0 +1,4 @@ +mod contract; +mod contract_tests; +mod multi_tests; +mod unit_tests; diff --git a/contracts/cw721-base/src/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs similarity index 64% rename from contracts/cw721-base/src/multi_tests.rs rename to packages/cw721/src/testing/multi_tests.rs index 951d5fe0f..f1fbe7b0a 100644 --- a/contracts/cw721-base/src/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -1,21 +1,93 @@ -use cosmwasm_std::{to_json_binary, Addr, Empty, QuerierWrapper, WasmMsg}; -use cw721::OwnerOfResponse; +use crate::{ + error::Cw721ContractError, + execute::Cw721Execute, + msg::{Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, Cw721QueryMsg, MinterResponse}, + query::{Cw721Query, OwnerOfResponse}, + state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, +}; +use cosmwasm_std::{ + to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, QuerierWrapper, Response, + StdResult, WasmMsg, +}; use cw_multi_test::{App, Contract, ContractWrapper, Executor}; use cw_ownable::{Ownership, OwnershipError}; +use cw_utils::Expiration; -use crate::{msg::MigrateMsg, ContractError, MinterResponse}; +use super::contract::Cw721Contract; pub const CREATOR_ADDR: &str = "creator"; pub const MINTER_ADDR: &str = "minter"; pub const OTHER_ADDR: &str = "other"; +pub const NFT_OWNER_ADDR: &str = "nft_owner"; + +pub fn instantiate( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: Cw721InstantiateMsg, +) -> Result { + let contract = Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + contract.instantiate(deps, env, info, msg, "contract_name", "contract_version") +} + +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: Cw721ExecuteMsg< + DefaultOptionMetadataExtension, + Empty, + DefaultOptionCollectionInfoExtension, + >, +) -> Result { + let contract = Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + contract.execute(deps, env, info, msg) +} + +pub fn query( + deps: Deps, + env: Env, + msg: Cw721QueryMsg, +) -> StdResult { + let contract = Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + contract.query(deps, env, msg) +} + +pub fn migrate( + deps: DepsMut, + env: Env, + msg: Cw721MigrateMsg, +) -> Result { + let contract = Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + contract.migrate(deps, env, msg, "contract_name", "contract_version") +} fn cw721_base_latest_contract() -> Box> { - let contract = ContractWrapper::new( - crate::entry::execute, - crate::entry::instantiate, - crate::entry::query, - ) - .with_migrate(crate::entry::migrate); + let contract = ContractWrapper::new(execute, instantiate, query).with_migrate(migrate); Box::new(contract) } @@ -53,7 +125,7 @@ fn query_owner(querier: QuerierWrapper, cw721: &Addr, token_id: String) -> Addr let resp: OwnerOfResponse = querier .query_wasm_smart( cw721, - &crate::QueryMsg::::OwnerOf { + &Cw721QueryMsg::::OwnerOf { token_id, include_expired: None, }, @@ -66,7 +138,7 @@ fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: St app.execute_contract( sender.clone(), cw721.clone(), - &crate::ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: token_id.clone(), owner: sender.to_string(), token_uri: None, @@ -82,7 +154,7 @@ fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: St app.execute_contract( sender, cw721.clone(), - &crate::ExecuteMsg::::TransferNft { + &Cw721ExecuteMsg::::TransferNft { recipient: "burner".to_string(), token_id: token_id.clone(), }, @@ -96,12 +168,193 @@ fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: St app.execute_contract( Addr::unchecked("burner"), cw721, - &crate::ExecuteMsg::::Burn { token_id }, + &Cw721ExecuteMsg::::Burn { token_id }, &[], ) .unwrap(); } +#[test] +fn test_operator() { + // --- setup --- + let mut app = App::default(); + let admin = Addr::unchecked("admin"); + let code_id = app.store_code(cw721_base_latest_contract()); + let other = Addr::unchecked(OTHER_ADDR); + let cw721 = app + .instantiate_contract( + code_id, + other.clone(), + &Cw721InstantiateMsg:: { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: Some(MINTER_ADDR.to_string()), + creator: Some(CREATOR_ADDR.to_string()), + collection_info_extension: None, + withdraw_address: None, + }, + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); + // mint + let minter = Addr::unchecked(MINTER_ADDR); + let nft_owner = Addr::unchecked(NFT_OWNER_ADDR); + app.execute_contract( + minter.clone(), + cw721.clone(), + &Cw721ExecuteMsg::::Mint { + token_id: "1".to_string(), + owner: nft_owner.to_string(), + token_uri: None, + extension: Empty::default(), + }, + &[], + ) + .unwrap(); + + // --- test operator/approve all --- + // owner adds other user as operator using approve all + app.execute_contract( + nft_owner.clone(), + cw721.clone(), + &Cw721ExecuteMsg::::ApproveAll { + operator: other.to_string(), + expires: Some(Expiration::Never {}), + }, + &[], + ) + .unwrap(); + + // transfer by operator + app.execute_contract( + other.clone(), + cw721.clone(), + &Cw721ExecuteMsg::::TransferNft { + recipient: other.to_string(), + token_id: "1".to_string(), + }, + &[], + ) + .unwrap(); + // check other is new owner + let owner_response: OwnerOfResponse = app + .wrap() + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::OwnerOf { + token_id: "1".to_string(), + include_expired: None, + }, + ) + .unwrap(); + assert_eq!(owner_response.owner, other.to_string()); + // check previous owner cant transfer + let err: Cw721ContractError = app + .execute_contract( + nft_owner.clone(), + cw721.clone(), + &Cw721ExecuteMsg::::TransferNft { + recipient: other.to_string(), + token_id: "1".to_string(), + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); + + // transfer back to previous owner + app.execute_contract( + other.clone(), + cw721.clone(), + &Cw721ExecuteMsg::::TransferNft { + recipient: nft_owner.to_string(), + token_id: "1".to_string(), + }, + &[], + ) + .unwrap(); + // check owner + let owner_response: OwnerOfResponse = app + .wrap() + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::OwnerOf { + token_id: "1".to_string(), + include_expired: None, + }, + ) + .unwrap(); + assert_eq!(owner_response.owner, nft_owner.to_string()); + + // other user is still operator and can transfer! + app.execute_contract( + other.clone(), + cw721.clone(), + &Cw721ExecuteMsg::::TransferNft { + recipient: other.to_string(), + token_id: "1".to_string(), + }, + &[], + ) + .unwrap(); + // check other is new owner + let owner_response: OwnerOfResponse = app + .wrap() + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::OwnerOf { + token_id: "1".to_string(), + include_expired: None, + }, + ) + .unwrap(); + assert_eq!(owner_response.owner, other.to_string()); + + // -- test revoke + // transfer to previous owner + app.execute_contract( + other.clone(), + cw721.clone(), + &Cw721ExecuteMsg::::TransferNft { + recipient: nft_owner.to_string(), + token_id: "1".to_string(), + }, + &[], + ) + .unwrap(); + + // revoke operator + app.execute_contract( + nft_owner.clone(), + cw721.clone(), + &Cw721ExecuteMsg::::RevokeAll { + operator: other.to_string(), + }, + &[], + ) + .unwrap(); + + // other not operator anymore and cant send + let err: Cw721ContractError = app + .execute_contract( + other.clone(), + cw721.clone(), + &Cw721ExecuteMsg::::TransferNft { + recipient: other.to_string(), + token_id: "1".to_string(), + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); +} + /// Instantiates a 0.16 version of this contract and tests that tokens /// can be minted, transferred, and burnred after migration. #[test] @@ -145,7 +398,7 @@ fn test_migration_legacy_to_latest() { WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, - msg: to_json_binary(&MigrateMsg::WithUpdate { + msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { minter: None, creator: None, }) @@ -157,11 +410,11 @@ fn test_migration_legacy_to_latest() { // non-minter user cant mint let other = Addr::unchecked(OTHER_ADDR); - let err: ContractError = app + let err: Cw721ContractError = app .execute_contract( other.clone(), cw721.clone(), - &crate::ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: other.to_string(), token_uri: None, @@ -172,7 +425,7 @@ fn test_migration_legacy_to_latest() { .unwrap_err() .downcast() .unwrap(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // legacy minter can still mint mint_transfer_and_burn( @@ -185,7 +438,7 @@ fn test_migration_legacy_to_latest() { // check new mint query response works. let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); @@ -193,14 +446,17 @@ fn test_migration_legacy_to_latest() { // is not None. let m: v16::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, legacy_creator_and_minter.to_string()); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetMinterOwnership {}, + ) .unwrap(); assert_eq!( minter_ownership.owner, @@ -210,7 +466,10 @@ fn test_migration_legacy_to_latest() { // check creator ownership query works let creator_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetCreatorOwnership {}, + ) .unwrap(); assert_eq!(creator_ownership.owner, Some(legacy_creator_and_minter)); } @@ -253,7 +512,7 @@ fn test_migration_legacy_to_latest() { WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, - msg: to_json_binary(&MigrateMsg::WithUpdate { + msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { minter: Some(MINTER_ADDR.to_string()), creator: Some(CREATOR_ADDR.to_string()), }) @@ -264,11 +523,11 @@ fn test_migration_legacy_to_latest() { .unwrap(); // legacy minter user cant mint - let err: ContractError = app + let err: Cw721ContractError = app .execute_contract( legacy_creator_and_minter.clone(), cw721.clone(), - &crate::ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: legacy_creator_and_minter.to_string(), token_uri: None, @@ -279,7 +538,7 @@ fn test_migration_legacy_to_latest() { .unwrap_err() .downcast() .unwrap(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // new minter can mint let minter = Addr::unchecked(MINTER_ADDR); @@ -288,7 +547,7 @@ fn test_migration_legacy_to_latest() { // check new mint query response works. let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(minter.to_string())); @@ -296,14 +555,17 @@ fn test_migration_legacy_to_latest() { // is not None. let m: v16::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, minter.to_string()); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetMinterOwnership {}, + ) .unwrap(); assert_eq!(minter_ownership.owner, Some(minter.clone())); @@ -311,7 +573,10 @@ fn test_migration_legacy_to_latest() { let creator = Addr::unchecked(CREATOR_ADDR); let creator_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetCreatorOwnership {}, + ) .unwrap(); assert_eq!(creator_ownership.owner, Some(creator)); } @@ -354,7 +619,7 @@ fn test_migration_legacy_to_latest() { WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, - msg: to_json_binary(&MigrateMsg::WithUpdate { + msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { minter: None, creator: None, }) @@ -366,11 +631,11 @@ fn test_migration_legacy_to_latest() { // non-minter user cant mint let other = Addr::unchecked(OTHER_ADDR); - let err: ContractError = app + let err: Cw721ContractError = app .execute_contract( other.clone(), cw721.clone(), - &crate::ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: other.to_string(), token_uri: None, @@ -381,7 +646,7 @@ fn test_migration_legacy_to_latest() { .unwrap_err() .downcast() .unwrap(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // legacy minter can still mint mint_transfer_and_burn( @@ -394,7 +659,7 @@ fn test_migration_legacy_to_latest() { // check new mint query response works. let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); @@ -402,14 +667,17 @@ fn test_migration_legacy_to_latest() { // is not None. let m: v17::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetMinterOwnership {}, + ) .unwrap(); assert_eq!( minter_ownership.owner, @@ -419,7 +687,10 @@ fn test_migration_legacy_to_latest() { // check creator ownership query works let creator_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetCreatorOwnership {}, + ) .unwrap(); assert_eq!(creator_ownership.owner, Some(legacy_creator_and_minter)); } @@ -462,7 +733,7 @@ fn test_migration_legacy_to_latest() { WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, - msg: to_json_binary(&MigrateMsg::WithUpdate { + msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { minter: Some(MINTER_ADDR.to_string()), creator: Some(CREATOR_ADDR.to_string()), }) @@ -473,11 +744,11 @@ fn test_migration_legacy_to_latest() { .unwrap(); // legacy minter user cant mint - let err: ContractError = app + let err: Cw721ContractError = app .execute_contract( legacy_creator_and_minter.clone(), cw721.clone(), - &crate::ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: legacy_creator_and_minter.to_string(), token_uri: None, @@ -488,7 +759,7 @@ fn test_migration_legacy_to_latest() { .unwrap_err() .downcast() .unwrap(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // new minter can mint let minter = Addr::unchecked(MINTER_ADDR); @@ -497,7 +768,7 @@ fn test_migration_legacy_to_latest() { // check new mint query response works. let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(minter.to_string())); @@ -505,14 +776,17 @@ fn test_migration_legacy_to_latest() { // is not None. let m: v17::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(minter.to_string())); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetMinterOwnership {}, + ) .unwrap(); assert_eq!(minter_ownership.owner, Some(minter.clone())); @@ -520,7 +794,10 @@ fn test_migration_legacy_to_latest() { let creator = Addr::unchecked(CREATOR_ADDR); let creator_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetCreatorOwnership {}, + ) .unwrap(); assert_eq!(creator_ownership.owner, Some(creator)); } @@ -563,7 +840,7 @@ fn test_migration_legacy_to_latest() { WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, - msg: to_json_binary(&MigrateMsg::WithUpdate { + msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { minter: None, creator: None, }) @@ -575,11 +852,11 @@ fn test_migration_legacy_to_latest() { // non-minter user cant mint let other = Addr::unchecked(OTHER_ADDR); - let err: ContractError = app + let err: Cw721ContractError = app .execute_contract( other.clone(), cw721.clone(), - &crate::ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: other.to_string(), token_uri: None, @@ -590,7 +867,7 @@ fn test_migration_legacy_to_latest() { .unwrap_err() .downcast() .unwrap(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // legacy minter can still mint mint_transfer_and_burn( @@ -603,7 +880,7 @@ fn test_migration_legacy_to_latest() { // check new mint query response works. let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); @@ -611,14 +888,17 @@ fn test_migration_legacy_to_latest() { // is not None. let m: v18::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetMinterOwnership {}, + ) .unwrap(); assert_eq!( minter_ownership.owner, @@ -628,11 +908,14 @@ fn test_migration_legacy_to_latest() { // check creator ownership query works let creator_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetCreatorOwnership {}, + ) .unwrap(); assert_eq!(creator_ownership.owner, Some(legacy_creator_and_minter)); } - // case 4: migrate from v0.18 to latest by providing new creator and minter addr + // case 6: migrate from v0.18 to latest by providing new creator and minter addr { use cw721_base_018 as v18; let mut app = App::default(); @@ -671,7 +954,7 @@ fn test_migration_legacy_to_latest() { WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, - msg: to_json_binary(&MigrateMsg::WithUpdate { + msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { minter: Some(MINTER_ADDR.to_string()), creator: Some(CREATOR_ADDR.to_string()), }) @@ -682,11 +965,11 @@ fn test_migration_legacy_to_latest() { .unwrap(); // legacy minter user cant mint - let err: ContractError = app + let err: Cw721ContractError = app .execute_contract( legacy_creator_and_minter.clone(), cw721.clone(), - &crate::ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: legacy_creator_and_minter.to_string(), token_uri: None, @@ -697,7 +980,7 @@ fn test_migration_legacy_to_latest() { .unwrap_err() .downcast() .unwrap(); - assert_eq!(err, ContractError::Ownership(OwnershipError::NotOwner)); + assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // new minter can mint let minter = Addr::unchecked(MINTER_ADDR); @@ -706,7 +989,7 @@ fn test_migration_legacy_to_latest() { // check new mint query response works. let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(minter.to_string())); @@ -714,14 +997,17 @@ fn test_migration_legacy_to_latest() { // is not None. let m: v18::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(minter.to_string())); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetMinterOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetMinterOwnership {}, + ) .unwrap(); assert_eq!(minter_ownership.owner, Some(minter.clone())); @@ -729,7 +1015,10 @@ fn test_migration_legacy_to_latest() { let creator = Addr::unchecked(CREATOR_ADDR); let creator_ownership: Ownership = app .wrap() - .query_wasm_smart(&cw721, &crate::QueryMsg::::GetCreatorOwnership {}) + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::::GetCreatorOwnership {}, + ) .unwrap(); assert_eq!(creator_ownership.owner, Some(creator)); } @@ -763,7 +1052,7 @@ fn test_instantiate_016_msg() { // assert withdraw address is None let withdraw_addr: Option = app .wrap() - .query_wasm_smart(cw721, &crate::QueryMsg::::GetWithdrawAddress {}) + .query_wasm_smart(cw721, &Cw721QueryMsg::::GetWithdrawAddress {}) .unwrap(); assert!(withdraw_addr.is_none()); } diff --git a/packages/cw721/src/testing/unit_tests.rs b/packages/cw721/src/testing/unit_tests.rs new file mode 100644 index 000000000..fb6f7768f --- /dev/null +++ b/packages/cw721/src/testing/unit_tests.rs @@ -0,0 +1,336 @@ +use crate::{ + execute::Cw721Execute, + msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}, + query::{Cw721Query, MAX_LIMIT}, + state::{ + token_owner_idx, CollectionInfo, DefaultOptionCollectionInfoExtension, + DefaultOptionMetadataExtension, Metadata, NftInfo, TokenIndexes, CREATOR, MINTER, + }, +}; +use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info}, + Addr, Empty, Order, StdResult, +}; +use cw2::ContractVersion; +use cw_storage_plus::{IndexedMap, Item, MultiIndex}; +use unit_tests::{contract::Cw721Contract, multi_tests::CREATOR_ADDR}; + +use super::*; + +/// Make sure cw2 version info is properly initialized during instantiation. +#[test] +fn proper_cw2_initialization() { + let mut deps = mock_dependencies(); + + Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default() + .instantiate( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + Cw721InstantiateMsg { + name: "collection_name".into(), + symbol: "collection_symbol".into(), + collection_info_extension: None, + minter: Some("minter".into()), + creator: Some("creator".into()), + withdraw_address: None, + }, + "contract_name", + "contract_version", + ) + .unwrap(); + + let minter = MINTER + .get_ownership(deps.as_ref().storage) + .unwrap() + .owner + .map(|a| a.into_string()); + assert_eq!(minter, Some("minter".to_string())); + + let creator = CREATOR + .get_ownership(deps.as_ref().storage) + .unwrap() + .owner + .map(|a| a.into_string()); + assert_eq!(creator, Some("creator".to_string())); + + let version = cw2::get_contract_version(deps.as_ref().storage).unwrap(); + assert_eq!( + version, + ContractVersion { + contract: "contract_name".into(), + version: "contract_version".into(), + }, + ); +} + +#[test] +fn proper_owner_initialization() { + let mut deps = mock_dependencies(); + + let info_owner = mock_info("owner", &[]); + Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default() + .instantiate( + deps.as_mut(), + mock_env(), + info_owner.clone(), + Cw721InstantiateMsg { + name: "collection_name".into(), + symbol: "collection_symbol".into(), + collection_info_extension: None, + creator: None, + minter: None, + withdraw_address: None, + }, + "contract_name", + "contract_version", + ) + .unwrap(); + + let minter = MINTER.item.load(deps.as_ref().storage).unwrap().owner; + assert_eq!(minter, Some(info_owner.sender)); + let creator = CREATOR.item.load(deps.as_ref().storage).unwrap().owner; + assert_eq!(creator, Some(Addr::unchecked("owner"))); +} + +#[test] +fn use_metadata_extension() { + let mut deps = mock_dependencies(); + let contract = Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + + let info = mock_info(CREATOR_ADDR, &[]); + let init_msg = Cw721InstantiateMsg { + name: "collection_name".into(), + symbol: "collection_symbol".into(), + collection_info_extension: None, + minter: None, + creator: None, + withdraw_address: None, + }; + let env = mock_env(); + contract + .instantiate( + deps.as_mut(), + env.clone(), + info.clone(), + init_msg, + "contract_name", + "contract_version", + ) + .unwrap(); + + let token_id = "Enterprise"; + let token_uri = Some("https://starships.example.com/Starship/Enterprise.json".into()); + let extension = Some(Metadata { + description: Some("Spaceship with Warp Drive".into()), + name: Some("Starship USS Enterprise".to_string()), + ..Metadata::default() + }); + let exec_msg = Cw721ExecuteMsg::Mint { + token_id: token_id.to_string(), + owner: "john".to_string(), + token_uri: token_uri.clone(), + extension: extension.clone(), + }; + contract + .execute(deps.as_mut(), env.clone(), info, exec_msg) + .unwrap(); + + let res = contract + .query_nft_info(deps.as_ref(), env, token_id.into()) + .unwrap(); + assert_eq!(res.token_uri, token_uri); + assert_eq!(res.extension, extension); +} + +#[test] +fn test_migrate() { + let mut deps = mock_dependencies(); + + let env = mock_env(); + use cw721_base_016 as v16; + v16::entry::instantiate( + deps.as_mut(), + env.clone(), + mock_info("owner", &[]), + v16::InstantiateMsg { + name: "legacy_name".into(), + symbol: "legacy_symbol".into(), + minter: "legacy_minter".into(), + }, + ) + .unwrap(); + + // mint 200 NFTs before migration + for i in 0..200 { + let info = mock_info("legacy_minter", &[]); + let msg = v16::ExecuteMsg::Mint(v16::msg::MintMsg { + token_id: i.to_string(), + owner: "owner".into(), + token_uri: None, + extension: None, + }); + v16::entry::execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + } + + // assert new data before migration: + // - ownership and collection info throws NotFound Error + MINTER.item.load(deps.as_ref().storage).unwrap_err(); // cw_ownable in v16 is used for minter + let contract = Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default(); + contract + .query_collection_info(deps.as_ref(), env.clone()) + .unwrap_err(); + // query on minter and creator store also throws NotFound Error + MINTER.get_ownership(deps.as_ref().storage).unwrap_err(); + CREATOR.get_ownership(deps.as_ref().storage).unwrap_err(); + // - no tokens + let all_tokens = contract + .query_all_tokens(deps.as_ref(), env.clone(), None, Some(MAX_LIMIT)) + .unwrap(); + assert_eq!(all_tokens.tokens.len(), 0); + + // assert legacy data before migration: + // - version + let version = cw2::get_contract_version(deps.as_ref().storage) + .unwrap() + .version; + assert_eq!(version, "0.16.0"); + // - legacy minter is set + let legacy_minter_store: Item = Item::new("minter"); + let legacy_minter = legacy_minter_store.load(deps.as_ref().storage).unwrap(); + assert_eq!(legacy_minter, "legacy_minter"); + // - legacy collection info is set + let legacy_collection_info_store: Item = Item::new("nft_info"); + let legacy_collection_info = legacy_collection_info_store + .load(deps.as_ref().storage) + .unwrap(); + assert_eq!(legacy_collection_info.name, "legacy_name"); + assert_eq!(legacy_collection_info.symbol, "legacy_symbol"); + // - legacy tokens are set + let indexes = TokenIndexes { + owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"), + }; + let legacy_tokens_store: IndexedMap< + &str, + NftInfo, + TokenIndexes, + > = IndexedMap::new("tokens", indexes); + let keys = legacy_tokens_store + .keys(deps.as_ref().storage, None, None, Order::Ascending) + .collect::>>() + .unwrap(); + assert_eq!(keys.len(), 200); + for key in keys { + let legacy_token = legacy_tokens_store + .load(deps.as_ref().storage, &key) + .unwrap(); + assert_eq!(legacy_token.owner.as_str(), "owner"); + } + + Cw721Contract::< + DefaultOptionMetadataExtension, + Empty, + Empty, + Empty, + DefaultOptionCollectionInfoExtension, + >::default() + .migrate( + deps.as_mut(), + env.clone(), + crate::msg::Cw721MigrateMsg::WithUpdate { + minter: None, + creator: None, + }, + "contract_name", + "contract_version", + ) + .unwrap(); + + // version + let version = cw2::get_contract_version(deps.as_ref().storage) + .unwrap() + .version; + assert_eq!(version, "contract_version"); + assert_ne!(version, "0.16.0"); + + // assert minter ownership + let minter_ownership = MINTER + .get_ownership(deps.as_ref().storage) + .unwrap() + .owner + .map(|a| a.into_string()); + assert_eq!(minter_ownership, Some("legacy_minter".to_string())); + + // assert creator ownership + let creator_ownership = CREATOR + .get_ownership(deps.as_ref().storage) + .unwrap() + .owner + .map(|a| a.into_string()); + assert_eq!(creator_ownership, Some("legacy_minter".to_string())); + + // assert collection info + let collection_info = contract + .query_collection_info(deps.as_ref(), env.clone()) + .unwrap(); + let legacy_contract_info = CollectionInfo { + name: "legacy_name".to_string(), + symbol: "legacy_symbol".to_string(), + extension: None, + updated_at: env.block.time, + }; + assert_eq!(collection_info, legacy_contract_info); + + // assert tokens + let all_tokens = contract + .query_all_tokens(deps.as_ref(), env, None, Some(MAX_LIMIT)) + .unwrap(); + assert_eq!(all_tokens.tokens.len(), 200); + + // assert legacy data is still there (allowing backward migration in case of issues) + // - minter + let legacy_minter = legacy_minter_store.load(deps.as_ref().storage).unwrap(); + assert_eq!(legacy_minter, "legacy_minter"); + // - collection info + let legacy_collection_info = legacy_collection_info_store + .load(deps.as_ref().storage) + .unwrap(); + assert_eq!(legacy_collection_info.name, "legacy_name"); + assert_eq!(legacy_collection_info.symbol, "legacy_symbol"); + // - tokens + let keys = legacy_tokens_store + .keys(deps.as_ref().storage, None, None, Order::Ascending) + .collect::>>() + .unwrap(); + assert_eq!(keys.len(), 200); + for key in keys { + let legacy_token = legacy_tokens_store + .load(deps.as_ref().storage, &key) + .unwrap(); + assert_eq!(legacy_token.owner.as_str(), "owner"); + } +} diff --git a/packages/cw721/src/traits.rs b/packages/cw721/src/traits.rs deleted file mode 100644 index 66ee87fd3..000000000 --- a/packages/cw721/src/traits.rs +++ /dev/null @@ -1,171 +0,0 @@ -use serde::de::DeserializeOwned; -use serde::Serialize; - -use crate::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfo, NftInfoResponse, - NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, -}; -use cosmwasm_std::{Binary, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; -use cw_utils::Expiration; - -pub trait Cw721: - Cw721Execute + Cw721Query -where - TMetadata: Serialize + DeserializeOwned + Clone, - TCustomResponseMessage: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, -{ -} - -pub trait Cw721Execute -where - TMetadata: Serialize + DeserializeOwned + Clone, - TCustomResponseMessage: CustomMsg, -{ - type Err: ToString; - - fn transfer_nft( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - recipient: String, - token_id: String, - ) -> Result, Self::Err>; - - fn send_nft( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - contract: String, - token_id: String, - msg: Binary, - ) -> Result, Self::Err>; - - fn approve( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - spender: String, - token_id: String, - expires: Option, - ) -> Result, Self::Err>; - - fn revoke( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - spender: String, - token_id: String, - ) -> Result, Self::Err>; - - fn approve_all( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - operator: String, - expires: Option, - ) -> Result, Self::Err>; - - fn revoke_all( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - operator: String, - ) -> Result, Self::Err>; - - fn burn( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - token_id: String, - ) -> Result, Self::Err>; -} - -pub trait Cw721Query -where - TMetadata: Serialize + DeserializeOwned + Clone, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, -{ - // TODO: use custom error? - // How to handle the two derived error types? - - fn collection_info(&self, deps: Deps) -> StdResult>; - - fn num_tokens(&self, deps: Deps) -> StdResult; - - fn nft_info(&self, deps: Deps, token_id: String) -> StdResult>; - - fn owner_of( - &self, - deps: Deps, - env: Env, - token_id: String, - include_expired: bool, - ) -> StdResult; - - fn operator( - &self, - deps: Deps, - env: Env, - owner: String, - operator: String, - include_expired: bool, - ) -> StdResult; - - fn operators( - &self, - deps: Deps, - env: Env, - owner: String, - include_expired: bool, - start_after: Option, - limit: Option, - ) -> StdResult; - - fn approval( - &self, - deps: Deps, - env: Env, - token_id: String, - spender: String, - include_expired: bool, - ) -> StdResult; - - fn approvals( - &self, - deps: Deps, - env: Env, - token_id: String, - include_expired: bool, - ) -> StdResult; - - fn tokens( - &self, - deps: Deps, - owner: String, - start_after: Option, - limit: Option, - ) -> StdResult; - - fn all_tokens( - &self, - deps: Deps, - start_after: Option, - limit: Option, - ) -> StdResult; - - fn all_nft_info( - &self, - deps: Deps, - env: Env, - token_id: String, - include_expired: bool, - ) -> StdResult>; -} From 895f46201fb1b3e35123f950a3e6cfd1ab8ec161 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 7 Mar 2024 21:08:54 +0100 Subject: [PATCH 17/47] fix ci --- .circleci/config.yml | 45 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cee9ad8f9..479bafbd7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,8 +4,9 @@ workflows: test: jobs: - contract_cw721_base - - contract_cw721_metadata_onchain + - contract_cw721_expiration - contract_cw721_fixed_price + - contract_cw721_receiver_tester - package_cw721 - lint - wasm-build @@ -37,6 +38,30 @@ jobs: environment: RUST_BACKTRACE: 1 command: cargo unit-test --locked + - save_cache: + paths: + - /usr/local/cargo/registry + - target + key: cargocache-cw721-base-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + + contract_cw721_expiration: + docker: + - image: rust:1.65.0 + working_directory: ~/project/contracts/cw721-expiration + steps: + - checkout: + path: ~/project + - run: + name: Version information + command: rustc --version; cargo --version; rustup --version + - restore_cache: + keys: + - cargocache-cw721-expiration-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + - run: + name: Unit Tests + environment: + RUST_BACKTRACE: 1 + command: cargo unit-test --locked - run: name: Build and run schema generator command: cargo schema --locked @@ -53,12 +78,12 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-base-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-expiration-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} - contract_cw721_metadata_onchain: + contract_cw721_fixed_price: docker: - image: rust:1.65.0 - working_directory: ~/project/contracts/cw721-metadata-onchain + working_directory: ~/project/contracts/cw721-fixed-price steps: - checkout: path: ~/project @@ -67,7 +92,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-cw721-metadata-onchain-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-cw721-fixed-price-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -89,12 +114,12 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-metadata-onchain-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-fixed-price-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} - contract_cw721_fixed_price: + contract_cw721_receiver_tester: docker: - image: rust:1.65.0 - working_directory: ~/project/contracts/cw721-fixed-price + working_directory: ~/project/contracts/cw721-receiver-tester steps: - checkout: path: ~/project @@ -103,7 +128,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-cw721-fixed-price-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-cw721-receiver-tester-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -125,7 +150,7 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-fixed-price-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-receiver-tester-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} package_cw721: docker: From 920a7d4d52d252857de216aab1b3712162fa0edd Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 7 Mar 2024 21:13:31 +0100 Subject: [PATCH 18/47] cargo fmt --- packages/cw721/examples/schema.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 2fe822c69..d0d514ee8 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -19,7 +19,11 @@ fn main() { create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); - export_schema_with_title(&schema_for!(Cw721InstantiateMsg), &out_dir, "InstantiateMsg"); + export_schema_with_title( + &schema_for!(Cw721InstantiateMsg), + &out_dir, + "InstantiateMsg", + ); export_schema_with_title( &schema_for!( Cw721ExecuteMsg::< @@ -29,9 +33,13 @@ fn main() { > ), &out_dir, - "ExecuteMsg" + "ExecuteMsg", + ); + export_schema_with_title( + &schema_for!(Cw721QueryMsg), + &out_dir, + "QueryMsg", ); - export_schema_with_title(&schema_for!(Cw721QueryMsg), &out_dir, "QueryMsg"); export_schema(&schema_for!(Cw721ReceiveMsg), &out_dir); export_schema_with_title( &schema_for!(NftInfoResponse), From 641ede9951092f55f83f2cb7112b204155d9235e Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 7 Mar 2024 21:22:06 +0100 Subject: [PATCH 19/47] cargo fmt + clippy --- contracts/cw721-expiration/src/contract_tests.rs | 4 +--- contracts/cw721-expiration/src/execute.rs | 4 ++-- contracts/cw721-expiration/src/lib.rs | 7 +++---- contracts/cw721-expiration/src/msg.rs | 2 +- contracts/cw721-expiration/src/query.rs | 5 ++--- contracts/cw721-fixed-price/src/contract.rs | 2 +- contracts/cw721-non-transferable/src/msg.rs | 1 + contracts/cw721-receiver-tester/tests/multitest.rs | 2 +- packages/cw721/src/state.rs | 2 +- packages/cw721/src/testing/contract_tests.rs | 4 ++-- packages/cw721/src/testing/multi_tests.rs | 12 ++++++++++++ 11 files changed, 27 insertions(+), 18 deletions(-) diff --git a/contracts/cw721-expiration/src/contract_tests.rs b/contracts/cw721-expiration/src/contract_tests.rs index 039ff49b7..303b4abb6 100644 --- a/contracts/cw721-expiration/src/contract_tests.rs +++ b/contracts/cw721-expiration/src/contract_tests.rs @@ -1,7 +1,5 @@ #![cfg(test)] -use std::ops::Add; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{ @@ -16,7 +14,7 @@ use cw721::query::{ }; use cw721::receiver::Cw721ReceiveMsg; use cw721::state::{CollectionInfo, DefaultOptionCollectionInfoExtension, CREATOR, MINTER}; -use cw721::{execute::Cw721Execute, query::Cw721Query, Approval, Expiration}; +use cw721::{query::Cw721Query, Approval, Expiration}; use cw_ownable::{Action, Ownership, OwnershipError}; use crate::state::Cw721ExpirationContract; diff --git a/contracts/cw721-expiration/src/execute.rs b/contracts/cw721-expiration/src/execute.rs index f03c95b3f..e4e4a173b 100644 --- a/contracts/cw721-expiration/src/execute.rs +++ b/contracts/cw721-expiration/src/execute.rs @@ -1,10 +1,9 @@ use cosmwasm_std::{ - Binary, CustomMsg, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, + Binary, CustomMsg, DepsMut, Env, MessageInfo, Response, }; use cw721::{ execute::Cw721Execute, msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}, - state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, Expiration, }; use serde::de::DeserializeOwned; @@ -127,6 +126,7 @@ where } } + #[allow(clippy::too_many_arguments)] pub fn mint_with_timestamp( &self, deps: DepsMut, diff --git a/contracts/cw721-expiration/src/lib.rs b/contracts/cw721-expiration/src/lib.rs index d744bdcb2..d225ee32d 100644 --- a/contracts/cw721-expiration/src/lib.rs +++ b/contracts/cw721-expiration/src/lib.rs @@ -9,7 +9,6 @@ mod contract_tests; use cosmwasm_std::Empty; use cw721::state::DefaultOptionMetadataExtension; -use cw721::{execute::Cw721Execute, query::Cw721Query}; // Version info for migration const CONTRACT_NAME: &str = "crates.io:cw721-expiration"; @@ -30,16 +29,16 @@ pub mod entry { #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; - use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; + use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response}; use cw721::{ - msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}, + msg::Cw721ExecuteMsg, state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, }; // This makes a conscious choice on the various generics used by the contract #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( - mut deps: DepsMut, + deps: DepsMut, env: Env, info: MessageInfo, msg: InstantiateMsg, diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index 5b827c18f..6e95da418 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -1,6 +1,6 @@ use crate::{DefaultOptionMetadataExtension, MinterResponse}; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Empty}; +use cosmwasm_std::{Addr}; use cw721::state::CollectionInfo; use cw_ownable::Ownership; use schemars::JsonSchema; diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index c7abd6cf5..db003a61a 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -1,14 +1,13 @@ -use cosmwasm_std::{to_json_binary, Addr, Binary, CustomMsg, Deps, Env, StdResult, Storage}; +use cosmwasm_std::{to_json_binary, Binary, CustomMsg, Deps, Env, StdResult}; use cw721::query::{ AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, Cw721Query, NftInfoResponse, - OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + OwnerOfResponse, TokensResponse, }; use serde::de::DeserializeOwned; use serde::Serialize; use crate::{ error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract, - DefaultOptionMetadataExtension, }; impl< diff --git a/contracts/cw721-fixed-price/src/contract.rs b/contracts/cw721-fixed-price/src/contract.rs index 4ae0c4b18..8ffc86b93 100644 --- a/contracts/cw721-fixed-price/src/contract.rs +++ b/contracts/cw721-fixed-price/src/contract.rs @@ -6,7 +6,7 @@ use crate::state::{Config, CONFIG}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Reply, + to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, ReplyOn, Response, StdResult, SubMsg, Uint128, WasmMsg, }; use cw2::set_contract_version; diff --git a/contracts/cw721-non-transferable/src/msg.rs b/contracts/cw721-non-transferable/src/msg.rs index c27b48746..6b921d9cb 100644 --- a/contracts/cw721-non-transferable/src/msg.rs +++ b/contracts/cw721-non-transferable/src/msg.rs @@ -98,6 +98,7 @@ impl From for Cw721QueryMsg { Cw721QueryMsg::AllTokens { start_after, limit } } + #[allow(deprecated)] QueryMsg::Minter {} => Cw721QueryMsg::Minter {}, QueryMsg::GetMinterOwnership {} => Cw721QueryMsg::GetMinterOwnership {}, QueryMsg::GetCreatorOwnership {} => Cw721QueryMsg::GetCreatorOwnership {}, diff --git a/contracts/cw721-receiver-tester/tests/multitest.rs b/contracts/cw721-receiver-tester/tests/multitest.rs index be1769fdb..3c074a908 100644 --- a/contracts/cw721-receiver-tester/tests/multitest.rs +++ b/contracts/cw721-receiver-tester/tests/multitest.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{to_json_binary, Addr, Attribute, Binary, Empty}; +use cosmwasm_std::{to_json_binary, Addr, Attribute, Binary}; use cw721::state::DefaultOptionCollectionInfoExtension; use cw_multi_test::{App, ContractWrapper, Executor}; diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index f824d3ff1..7cf654938 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, BlockInfo, CustomMsg, Decimal, Empty, StdResult, Storage, Timestamp}; +use cosmwasm_std::{Addr, BlockInfo, CustomMsg, Decimal, StdResult, Storage, Timestamp}; use cw_ownable::{OwnershipStore, OWNERSHIP_KEY}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; use cw_utils::Expiration; diff --git a/packages/cw721/src/testing/contract_tests.rs b/packages/cw721/src/testing/contract_tests.rs index 9382b83fa..b42f11051 100644 --- a/packages/cw721/src/testing/contract_tests.rs +++ b/packages/cw721/src/testing/contract_tests.rs @@ -2,7 +2,7 @@ use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{ - from_json, to_json_binary, Addr, Coin, CosmosMsg, CustomMsg, DepsMut, Empty, Response, + from_json, to_json_binary, Addr, Coin, CosmosMsg, DepsMut, Empty, Response, StdError, Timestamp, WasmMsg, }; @@ -13,7 +13,7 @@ use crate::query::{ }; use crate::receiver::Cw721ReceiveMsg; use crate::state::{ - CollectionInfo, Cw721Config, DefaultOptionCollectionInfoExtension, + CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension, CREATOR, MINTER, }; use crate::{execute::Cw721Execute, query::Cw721Query, Approval, Expiration}; diff --git a/packages/cw721/src/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index f1fbe7b0a..0989b14e6 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -436,6 +436,7 @@ fn test_migration_legacy_to_latest() { ); // check new mint query response works. + #[allow(deprecated)] let m: MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -444,6 +445,7 @@ fn test_migration_legacy_to_latest() { // check that the new response is backwards compatable when minter // is not None. + #[allow(deprecated)] let m: v16::MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -545,6 +547,7 @@ fn test_migration_legacy_to_latest() { mint_transfer_and_burn(&mut app, cw721.clone(), minter.clone(), "1".to_string()); // check new mint query response works. + #[allow(deprecated)] let m: MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -553,6 +556,7 @@ fn test_migration_legacy_to_latest() { // check that the new response is backwards compatable when minter // is not None. + #[allow(deprecated)] let m: v16::MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -657,6 +661,7 @@ fn test_migration_legacy_to_latest() { ); // check new mint query response works. + #[allow(deprecated)] let m: MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -665,6 +670,7 @@ fn test_migration_legacy_to_latest() { // check that the new response is backwards compatable when minter // is not None. + #[allow(deprecated)] let m: v17::MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -766,6 +772,7 @@ fn test_migration_legacy_to_latest() { mint_transfer_and_burn(&mut app, cw721.clone(), minter.clone(), "1".to_string()); // check new mint query response works. + #[allow(deprecated)] let m: MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -774,6 +781,7 @@ fn test_migration_legacy_to_latest() { // check that the new response is backwards compatable when minter // is not None. + #[allow(deprecated)] let m: v17::MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -878,6 +886,7 @@ fn test_migration_legacy_to_latest() { ); // check new mint query response works. + #[allow(deprecated)] let m: MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -886,6 +895,7 @@ fn test_migration_legacy_to_latest() { // check that the new response is backwards compatable when minter // is not None. + #[allow(deprecated)] let m: v18::MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -987,6 +997,7 @@ fn test_migration_legacy_to_latest() { mint_transfer_and_burn(&mut app, cw721.clone(), minter.clone(), "1".to_string()); // check new mint query response works. + #[allow(deprecated)] let m: MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) @@ -995,6 +1006,7 @@ fn test_migration_legacy_to_latest() { // check that the new response is backwards compatable when minter // is not None. + #[allow(deprecated)] let m: v18::MinterResponse = app .wrap() .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) From 0e27df83f168054bb43864766cd32c3c7ff5f717 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 7 Mar 2024 23:01:27 +0100 Subject: [PATCH 20/47] cargo fmt --- contracts/cw721-expiration/src/execute.rs | 4 +--- contracts/cw721-expiration/src/msg.rs | 2 +- contracts/cw721-expiration/src/query.rs | 4 +--- contracts/cw721-fixed-price/src/contract.rs | 4 ++-- packages/cw721/src/msg.rs | 14 ++++++++------ packages/cw721/src/testing/contract_tests.rs | 8 ++++---- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/contracts/cw721-expiration/src/execute.rs b/contracts/cw721-expiration/src/execute.rs index e4e4a173b..fd4e38004 100644 --- a/contracts/cw721-expiration/src/execute.rs +++ b/contracts/cw721-expiration/src/execute.rs @@ -1,6 +1,4 @@ -use cosmwasm_std::{ - Binary, CustomMsg, DepsMut, Env, MessageInfo, Response, -}; +use cosmwasm_std::{Binary, CustomMsg, DepsMut, Env, MessageInfo, Response}; use cw721::{ execute::Cw721Execute, msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}, diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index 6e95da418..37aa56816 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -1,6 +1,6 @@ use crate::{DefaultOptionMetadataExtension, MinterResponse}; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr}; +use cosmwasm_std::Addr; use cw721::state::CollectionInfo; use cw_ownable::Ownership; use schemars::JsonSchema; diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index db003a61a..39e98ef65 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -6,9 +6,7 @@ use cw721::query::{ use serde::de::DeserializeOwned; use serde::Serialize; -use crate::{ - error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract, -}; +use crate::{error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract}; impl< 'a, diff --git a/contracts/cw721-fixed-price/src/contract.rs b/contracts/cw721-fixed-price/src/contract.rs index 8ffc86b93..a365e7833 100644 --- a/contracts/cw721-fixed-price/src/contract.rs +++ b/contracts/cw721-fixed-price/src/contract.rs @@ -6,8 +6,8 @@ use crate::state::{Config, CONFIG}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, - ReplyOn, Response, StdResult, SubMsg, Uint128, WasmMsg, + to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, ReplyOn, Response, + StdResult, SubMsg, Uint128, WasmMsg, }; use cw2::set_contract_version; use cw20::Cw20ReceiveMsg; diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index 27f50715a..1c9d5c42a 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -214,17 +214,19 @@ pub enum Cw721QueryMsg #[returns(MinterResponse)] Minter {}, - /// Extension query + #[returns(Option)] + GetWithdrawAddress {}, + + // -- below queries, Extension and GetCollectionInfoExtension, are just dummies, since type annotations are required for + // -- TMetadataResponse and TCollectionInfoExtension, Error: + // -- "type annotations needed: cannot infer type for type parameter `TMetadataResponse` declared on the enum `Cw721QueryMsg`" + /// Do not use - dummy extension query, needed for inferring type parameter during compile #[returns(())] Extension { msg: TMetadataResponse }, - /// This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: - /// `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg` + /// Do not use - dummy collection info extension query, needed for inferring type parameter during compile #[returns(())] GetCollectionInfoExtension { msg: TCollectionInfoExtension }, - - #[returns(Option)] - GetWithdrawAddress {}, } /// Shows who can mint these tokens diff --git a/packages/cw721/src/testing/contract_tests.rs b/packages/cw721/src/testing/contract_tests.rs index b42f11051..623be873b 100644 --- a/packages/cw721/src/testing/contract_tests.rs +++ b/packages/cw721/src/testing/contract_tests.rs @@ -2,8 +2,8 @@ use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{ - from_json, to_json_binary, Addr, Coin, CosmosMsg, DepsMut, Empty, Response, - StdError, Timestamp, WasmMsg, + from_json, to_json_binary, Addr, Coin, CosmosMsg, DepsMut, Empty, Response, StdError, + Timestamp, WasmMsg, }; use crate::error::Cw721ContractError; @@ -13,8 +13,8 @@ use crate::query::{ }; use crate::receiver::Cw721ReceiveMsg; use crate::state::{ - CollectionInfo, DefaultOptionCollectionInfoExtension, - DefaultOptionMetadataExtension, CREATOR, MINTER, + CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension, CREATOR, + MINTER, }; use crate::{execute::Cw721Execute, query::Cw721Query, Approval, Expiration}; use crate::{CollectionInfoExtension, RoyaltyInfo}; From 8d91900a3a867dabdb04d134788ae80fea8ec363 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 7 Mar 2024 23:07:35 +0100 Subject: [PATCH 21/47] cargo schema --- packages/cw721/schema/query_msg.json | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/cw721/schema/query_msg.json b/packages/cw721/schema/query_msg.json index f587f0445..d18b5bc4c 100644 --- a/packages/cw721/schema/query_msg.json +++ b/packages/cw721/schema/query_msg.json @@ -381,7 +381,20 @@ "additionalProperties": false }, { - "description": "Extension query", + "type": "object", + "required": [ + "get_withdraw_address" + ], + "properties": { + "get_withdraw_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Do not use - dummy extension query, needed for inferring type parameter during compile", "type": "object", "required": [ "extension" @@ -403,7 +416,7 @@ "additionalProperties": false }, { - "description": "This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg`", + "description": "Do not use - dummy collection info extension query, needed for inferring type parameter during compile", "type": "object", "required": [ "get_collection_info_extension" @@ -427,19 +440,6 @@ } }, "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_withdraw_address" - ], - "properties": { - "get_withdraw_address": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false } ], "definitions": { From 9010cacd71324c63457dbabe4e1c1bf03354a442 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 7 Mar 2024 23:31:18 +0100 Subject: [PATCH 22/47] cargo clippy --- packages/cw721/src/testing/multi_tests.rs | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/cw721/src/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index 0989b14e6..790766736 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -202,7 +202,7 @@ fn test_operator() { let minter = Addr::unchecked(MINTER_ADDR); let nft_owner = Addr::unchecked(NFT_OWNER_ADDR); app.execute_contract( - minter.clone(), + minter, cw721.clone(), &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), @@ -329,7 +329,7 @@ fn test_operator() { // revoke operator app.execute_contract( - nft_owner.clone(), + nft_owner, cw721.clone(), &Cw721ExecuteMsg::::RevokeAll { operator: other.to_string(), @@ -342,7 +342,7 @@ fn test_operator() { let err: Cw721ContractError = app .execute_contract( other.clone(), - cw721.clone(), + cw721, &Cw721ExecuteMsg::::TransferNft { recipient: other.to_string(), token_id: "1".to_string(), @@ -394,7 +394,7 @@ fn test_migration_legacy_to_latest() { // migrate app.execute( - admin.clone(), + admin, WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, @@ -510,7 +510,7 @@ fn test_migration_legacy_to_latest() { // migrate app.execute( - admin.clone(), + admin, WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, @@ -571,7 +571,7 @@ fn test_migration_legacy_to_latest() { &Cw721QueryMsg::::GetMinterOwnership {}, ) .unwrap(); - assert_eq!(minter_ownership.owner, Some(minter.clone())); + assert_eq!(minter_ownership.owner, Some(minter)); // check creator ownership query works let creator = Addr::unchecked(CREATOR_ADDR); @@ -619,7 +619,7 @@ fn test_migration_legacy_to_latest() { // migrate app.execute( - admin.clone(), + admin, WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, @@ -735,7 +735,7 @@ fn test_migration_legacy_to_latest() { // migrate app.execute( - admin.clone(), + admin, WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, @@ -796,7 +796,7 @@ fn test_migration_legacy_to_latest() { &Cw721QueryMsg::::GetMinterOwnership {}, ) .unwrap(); - assert_eq!(minter_ownership.owner, Some(minter.clone())); + assert_eq!(minter_ownership.owner, Some(minter)); // check creator ownership query works let creator = Addr::unchecked(CREATOR_ADDR); @@ -844,7 +844,7 @@ fn test_migration_legacy_to_latest() { // migrate app.execute( - admin.clone(), + admin, WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, @@ -960,7 +960,7 @@ fn test_migration_legacy_to_latest() { // migrate app.execute( - admin.clone(), + admin, WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, @@ -1021,7 +1021,7 @@ fn test_migration_legacy_to_latest() { &Cw721QueryMsg::::GetMinterOwnership {}, ) .unwrap(); - assert_eq!(minter_ownership.owner, Some(minter.clone())); + assert_eq!(minter_ownership.owner, Some(minter)); // check creator ownership query works let creator = Addr::unchecked(CREATOR_ADDR); From bebe8b249fa381aa458906d21d2698e7de70225e Mon Sep 17 00:00:00 2001 From: mr-t Date: Fri, 8 Mar 2024 08:51:06 +0100 Subject: [PATCH 23/47] undo: do not rename token keys --- packages/cw721/src/execute.rs | 43 ++++------------------------------- packages/cw721/src/state.rs | 9 ++++---- 2 files changed, 9 insertions(+), 43 deletions(-) diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index 773106fe4..44b36df00 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -1,9 +1,9 @@ use cosmwasm_std::{ - Addr, Api, BankMsg, Binary, Coin, CustomMsg, Deps, DepsMut, Empty, Env, MessageInfo, Order, - Response, StdResult, Storage, + Addr, Api, BankMsg, Binary, Coin, CustomMsg, Deps, DepsMut, Empty, Env, MessageInfo, Response, + StdResult, Storage, }; use cw_ownable::{none_or, Action, Ownership, OwnershipError}; -use cw_storage_plus::{IndexedMap, Item, MultiIndex}; +use cw_storage_plus::Item; use cw_utils::Expiration; use serde::de::DeserializeOwned; use serde::Serialize; @@ -13,8 +13,8 @@ use crate::{ msg::{CollectionInfoMsg, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg}, receiver::Cw721ReceiveMsg, state::{ - token_owner_idx, CollectionInfo, Cw721Config, DefaultOptionCollectionInfoExtension, - DefaultOptionMetadataExtension, NftInfo, TokenIndexes, CREATOR, MINTER, + CollectionInfo, Cw721Config, DefaultOptionCollectionInfoExtension, + DefaultOptionMetadataExtension, NftInfo, CREATOR, MINTER, }, Approval, }; @@ -150,7 +150,6 @@ pub trait Cw721Execute< let response = migrate_legacy_minter_and_creator(deps.storage, deps.api, &env, &msg, response)?; let response = migrate_legacy_collection_info(deps.storage, &env, &msg, response)?; - let response = migrate_legacy_tokens(deps.storage, &env, &msg, response)?; // ... then migrate let response = migrate_version(deps.storage, contract_name, contract_version, response)?; // ... and update creator and minter AFTER legacy migration @@ -776,35 +775,3 @@ pub fn migrate_legacy_collection_info( } } } - -/// Migrates only in case collection_info is not present -pub fn migrate_legacy_tokens( - storage: &mut dyn Storage, - _env: &Env, - _msg: &Cw721MigrateMsg, - response: Response, -) -> StdResult { - let contract = - Cw721Config::::default(); - match contract.nft_info.is_empty(storage) { - false => Ok(response), - true => { - let indexes = TokenIndexes { - owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"), - }; - let legacy_tokens_store: IndexedMap< - &str, - NftInfo, - TokenIndexes, - > = IndexedMap::new("tokens", indexes); - let keys = legacy_tokens_store - .keys(storage, None, None, Order::Ascending) - .collect::>>()?; - for key in keys { - let legacy_token = legacy_tokens_store.load(storage, &key)?; - contract.nft_info.save(storage, &key, &legacy_token)?; - } - Ok(response) - } - } -} diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index 7cf654938..c2fd0a260 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -33,13 +33,12 @@ pub struct Cw721Config< TExtensionExecuteMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { - /// Note: do not use deprecated/legacy key "nft_info"! + /// Note: replaces deprecated/legacy key "nft_info"! pub collection_info: Item<'a, CollectionInfo>, pub token_count: Item<'a, u64>, /// Stored as (granter, operator) giving operator full control over granter's account. /// NOTE: granter is the owner, so operator has only control for NFTs owned by granter! pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, - /// Note: do not use deprecated/legacy keys "tokens" and "tokens__owner"! pub nft_info: IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, TMetadata>>, pub withdraw_address: Item<'a, String>, @@ -71,11 +70,11 @@ where { fn default() -> Self { Self::new( - "collection_info", // Note: do not use deprecated/legacy key "nft_info" + "collection_info", // Note: replaces deprecated/legacy key "nft_info" "num_tokens", "operators", - "nft", // Note: do not use deprecated/legacy key "tokens" - "nft__owner", // Note: do not use deprecated/legacy key "tokens__owner" + "tokens", + "tokens__owner", "withdraw_address", ) } From 3d77652f783d4100937fd0619e57f45b0742ddfa Mon Sep 17 00:00:00 2001 From: mr-t Date: Fri, 8 Mar 2024 09:14:57 +0100 Subject: [PATCH 24/47] fix unit test --- packages/cw721/src/testing/unit_tests.rs | 66 ++++++++---------------- 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/packages/cw721/src/testing/unit_tests.rs b/packages/cw721/src/testing/unit_tests.rs index fb6f7768f..a237fa6ca 100644 --- a/packages/cw721/src/testing/unit_tests.rs +++ b/packages/cw721/src/testing/unit_tests.rs @@ -3,16 +3,16 @@ use crate::{ msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}, query::{Cw721Query, MAX_LIMIT}, state::{ - token_owner_idx, CollectionInfo, DefaultOptionCollectionInfoExtension, - DefaultOptionMetadataExtension, Metadata, NftInfo, TokenIndexes, CREATOR, MINTER, + CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension, + Metadata, CREATOR, MINTER, }, }; use cosmwasm_std::{ testing::{mock_dependencies, mock_env, mock_info}, - Addr, Empty, Order, StdResult, + Addr, Empty, }; use cw2::ContractVersion; -use cw_storage_plus::{IndexedMap, Item, MultiIndex}; +use cw_storage_plus::Item; use unit_tests::{contract::Cw721Contract, multi_tests::CREATOR_ADDR}; use super::*; @@ -204,15 +204,9 @@ fn test_migrate() { contract .query_collection_info(deps.as_ref(), env.clone()) .unwrap_err(); - // query on minter and creator store also throws NotFound Error + // - query in new minter and creator ownership store throws NotFound Error (in v16 it was stored outside cw_ownable, in dedicated "minter" store) MINTER.get_ownership(deps.as_ref().storage).unwrap_err(); CREATOR.get_ownership(deps.as_ref().storage).unwrap_err(); - // - no tokens - let all_tokens = contract - .query_all_tokens(deps.as_ref(), env.clone(), None, Some(MAX_LIMIT)) - .unwrap(); - assert_eq!(all_tokens.tokens.len(), 0); - // assert legacy data before migration: // - version let version = cw2::get_contract_version(deps.as_ref().storage) @@ -225,30 +219,15 @@ fn test_migrate() { assert_eq!(legacy_minter, "legacy_minter"); // - legacy collection info is set let legacy_collection_info_store: Item = Item::new("nft_info"); - let legacy_collection_info = legacy_collection_info_store - .load(deps.as_ref().storage) - .unwrap(); - assert_eq!(legacy_collection_info.name, "legacy_name"); - assert_eq!(legacy_collection_info.symbol, "legacy_symbol"); - // - legacy tokens are set - let indexes = TokenIndexes { - owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"), - }; - let legacy_tokens_store: IndexedMap< - &str, - NftInfo, - TokenIndexes, - > = IndexedMap::new("tokens", indexes); - let keys = legacy_tokens_store - .keys(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() + let all_tokens = contract + .query_all_tokens(deps.as_ref(), env.clone(), None, Some(MAX_LIMIT)) .unwrap(); - assert_eq!(keys.len(), 200); - for key in keys { - let legacy_token = legacy_tokens_store - .load(deps.as_ref().storage, &key) + assert_eq!(all_tokens.tokens.len(), 200); + for token_id in 0..200 { + let token = contract + .query_owner_of(deps.as_ref(), env.clone(), token_id.to_string(), false) .unwrap(); - assert_eq!(legacy_token.owner.as_str(), "owner"); + assert_eq!(token.owner.as_str(), "owner"); } Cw721Contract::< @@ -301,13 +280,13 @@ fn test_migrate() { name: "legacy_name".to_string(), symbol: "legacy_symbol".to_string(), extension: None, - updated_at: env.block.time, + updated_at: env.clone().block.time, }; assert_eq!(collection_info, legacy_contract_info); // assert tokens let all_tokens = contract - .query_all_tokens(deps.as_ref(), env, None, Some(MAX_LIMIT)) + .query_all_tokens(deps.as_ref(), env.clone(), None, Some(MAX_LIMIT)) .unwrap(); assert_eq!(all_tokens.tokens.len(), 200); @@ -321,16 +300,15 @@ fn test_migrate() { .unwrap(); assert_eq!(legacy_collection_info.name, "legacy_name"); assert_eq!(legacy_collection_info.symbol, "legacy_symbol"); - // - tokens - let keys = legacy_tokens_store - .keys(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() + // - tokens are unchanged/still exist + let all_tokens = contract + .query_all_tokens(deps.as_ref(), env.clone(), None, Some(MAX_LIMIT)) .unwrap(); - assert_eq!(keys.len(), 200); - for key in keys { - let legacy_token = legacy_tokens_store - .load(deps.as_ref().storage, &key) + assert_eq!(all_tokens.tokens.len(), 200); + for token_id in 0..200 { + let token = contract + .query_owner_of(deps.as_ref(), env.clone(), token_id.to_string(), false) .unwrap(); - assert_eq!(legacy_token.owner.as_str(), "owner"); + assert_eq!(token.owner.as_str(), "owner"); } } From 6a827a32a9fc0b55a33d77f2fb0638bbb7642054 Mon Sep 17 00:00:00 2001 From: mr-t Date: Fri, 8 Mar 2024 11:23:57 +0100 Subject: [PATCH 25/47] remove useless generic `TMetadataResponse` --- Cargo.lock | 1 + contracts/cw2981-royalties/Cargo.toml | 1 + contracts/cw2981-royalties/examples/schema.rs | 2 +- .../schema/cw2981-royalties.json | 572 +++++++++++++----- contracts/cw2981-royalties/src/lib.rs | 42 +- contracts/cw2981-royalties/src/msg.rs | 208 ++++++- contracts/cw721-base/src/execute.rs | 23 +- contracts/cw721-base/src/lib.rs | 6 +- contracts/cw721-base/src/query.rs | 16 +- contracts/cw721-base/src/state.rs | 23 +- .../cw721-expiration/src/contract_tests.rs | 4 - contracts/cw721-expiration/src/execute.rs | 25 +- contracts/cw721-expiration/src/lib.rs | 6 +- contracts/cw721-expiration/src/msg.rs | 5 +- contracts/cw721-expiration/src/query.rs | 24 +- contracts/cw721-expiration/src/state.rs | 23 +- contracts/cw721-fixed-price/src/contract.rs | 14 +- contracts/cw721-non-transferable/src/lib.rs | 1 - contracts/cw721-non-transferable/src/msg.rs | 16 +- packages/cw721/src/execute.rs | 81 ++- packages/cw721/src/helpers.rs | 19 +- packages/cw721/src/msg.rs | 17 +- packages/cw721/src/query.rs | 192 ++---- packages/cw721/src/state.rs | 59 +- packages/cw721/src/testing/contract.rs | 62 +- packages/cw721/src/testing/contract_tests.rs | 6 +- packages/cw721/src/testing/multi_tests.rs | 6 +- packages/cw721/src/testing/unit_tests.rs | 5 - 28 files changed, 838 insertions(+), 621 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5289c452f..5485d000e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,6 +390,7 @@ version = "0.19.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", + "cw-ownable 0.6.0", "cw2 1.1.2", "cw721 0.19.0", "cw721-base 0.19.0", diff --git a/contracts/cw2981-royalties/Cargo.toml b/contracts/cw2981-royalties/Cargo.toml index d9140bc37..e66cb6284 100644 --- a/contracts/cw2981-royalties/Cargo.toml +++ b/contracts/cw2981-royalties/Cargo.toml @@ -21,6 +21,7 @@ library = [] [dependencies] cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } +cw-ownable = { workspace = true } cw2 = { workspace = true } cw721 = { workspace = true } cw721-base = { workspace = true, features = ["library"] } diff --git a/contracts/cw2981-royalties/examples/schema.rs b/contracts/cw2981-royalties/examples/schema.rs index 0400900bc..5aebaa36e 100644 --- a/contracts/cw2981-royalties/examples/schema.rs +++ b/contracts/cw2981-royalties/examples/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use cw2981_royalties::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cw2981_royalties::{msg::QueryMsg, ExecuteMsg, InstantiateMsg}; use cw721::state::DefaultOptionCollectionInfoExtension; fn main() { diff --git a/contracts/cw2981-royalties/schema/cw2981-royalties.json b/contracts/cw2981-royalties/schema/cw2981-royalties.json index b397a12d3..59f356efe 100644 --- a/contracts/cw2981-royalties/schema/cw2981-royalties.json +++ b/contracts/cw2981-royalties/schema/cw2981-royalties.json @@ -865,6 +865,46 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "QueryMsg", "oneOf": [ + { + "description": "Should be called on sale to see if royalties are owed by the marketplace selling the NFT, if CheckRoyalties returns true See https://eips.ethereum.org/EIPS/eip-2981", + "type": "object", + "required": [ + "royalty_info" + ], + "properties": { + "royalty_info": { + "type": "object", + "required": [ + "sale_price", + "token_id" + ], + "properties": { + "sale_price": { + "$ref": "#/definitions/Uint128" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Called against contract to determine if this NFT implements royalties. Should return a boolean as part of CheckRoyaltiesResponse - default can simply be true if royalties are implemented at token level (i.e. always check on sale)", + "type": "object", + "required": [ + "check_royalties" + ], + "properties": { + "check_royalties": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "Return the owner of the given token, error if token does not exist", "type": "object", @@ -1244,41 +1284,32 @@ "additionalProperties": false }, { - "description": "Extension query", "type": "object", "required": [ - "extension" + "get_withdraw_address" ], "properties": { - "extension": { + "get_withdraw_address": { "type": "object", - "required": [ - "msg" - ], - "properties": { - "msg": { - "$ref": "#/definitions/Cw2981QueryMsg" - } - }, "additionalProperties": false } }, "additionalProperties": false }, { - "description": "This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg`", + "description": "Do not use - dummy extension query, needed for inferring type parameter during compile", "type": "object", "required": [ - "get_collection_info_extension" + "extension" ], "properties": { - "get_collection_info_extension": { + "extension": { "type": "object", "properties": { "msg": { "anyOf": [ { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + "$ref": "#/definitions/Metadata" }, { "type": "null" @@ -1292,13 +1323,26 @@ "additionalProperties": false }, { + "description": "Do not use - dummy collection info extension query, needed for inferring type parameter during compile", "type": "object", "required": [ - "get_withdraw_address" + "get_collection_info_extension" ], "properties": { - "get_withdraw_address": { + "get_collection_info_extension": { "type": "object", + "properties": { + "msg": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + } + }, "additionalProperties": false } }, @@ -1358,53 +1402,88 @@ }, "additionalProperties": false }, - "Cw2981QueryMsg": { - "oneOf": [ - { - "description": "Should be called on sale to see if royalties are owed by the marketplace selling the NFT, if CheckRoyalties returns true See https://eips.ethereum.org/EIPS/eip-2981", - "type": "object", - "required": [ - "royalty_info" + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" ], - "properties": { - "royalty_info": { - "type": "object", - "required": [ - "sale_price", - "token_id" - ], - "properties": { - "sale_price": { - "$ref": "#/definitions/Uint128" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false + "items": { + "$ref": "#/definitions/Trait" + } }, - { - "description": "Called against contract to determine if this NFT implements royalties. Should return a boolean as part of CheckRoyaltiesResponse - default can simply be true if royalties are implemented at token level (i.e. always check on sale)", - "type": "object", - "required": [ - "check_royalties" + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "royalty_payment_address": { + "description": "The payment address, may be different to or the same as the minter addr question: how do we validate this?", + "type": [ + "string", + "null" + ] + }, + "royalty_percentage": { + "description": "This is how much the minter takes as a cut when sold royalties are owed on this token if it is Some", + "type": [ + "integer", + "null" ], - "properties": { - "check_royalties": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false + "format": "uint64", + "minimum": 0.0 + }, + "youtube_url": { + "type": [ + "string", + "null" + ] } - ] - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" + }, + "additionalProperties": false }, "RoyaltyInfo": { "type": "object", @@ -1430,6 +1509,28 @@ } ] }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", "type": "string" @@ -1445,7 +1546,7 @@ "responses": { "all_nft_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AllNftInfoResponse_for_Cw2981QueryMsg", + "title": "AllNftInfoResponse_for_Nullable_Metadata", "type": "object", "required": [ "access", @@ -1464,7 +1565,7 @@ "description": "Data on the token itself,", "allOf": [ { - "$ref": "#/definitions/NftInfoResponse_for_Cw2981QueryMsg" + "$ref": "#/definitions/NftInfoResponse_for_Nullable_Metadata" } ] } @@ -1501,50 +1602,6 @@ }, "additionalProperties": false }, - "Cw2981QueryMsg": { - "oneOf": [ - { - "description": "Should be called on sale to see if royalties are owed by the marketplace selling the NFT, if CheckRoyalties returns true See https://eips.ethereum.org/EIPS/eip-2981", - "type": "object", - "required": [ - "royalty_info" - ], - "properties": { - "royalty_info": { - "type": "object", - "required": [ - "sale_price", - "token_id" - ], - "properties": { - "sale_price": { - "$ref": "#/definitions/Uint128" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Called against contract to determine if this NFT implements royalties. Should return a boolean as part of CheckRoyaltiesResponse - default can simply be true if royalties are implemented at token level (i.e. always check on sale)", - "type": "object", - "required": [ - "check_royalties" - ], - "properties": { - "check_royalties": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, "Expiration": { "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", "oneOf": [ @@ -1592,17 +1649,96 @@ } ] }, - "NftInfoResponse_for_Cw2981QueryMsg": { + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "royalty_payment_address": { + "description": "The payment address, may be different to or the same as the minter addr question: how do we validate this?", + "type": [ + "string", + "null" + ] + }, + "royalty_percentage": { + "description": "This is how much the minter takes as a cut when sold royalties are owed on this token if it is Some", + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "NftInfoResponse_for_Nullable_Metadata": { "type": "object", - "required": [ - "extension" - ], "properties": { "extension": { "description": "You can add any custom metadata here when you extend cw721-base", - "allOf": [ + "anyOf": [ { - "$ref": "#/definitions/Cw2981QueryMsg" + "$ref": "#/definitions/Metadata" + }, + { + "type": "null" } ] }, @@ -1645,9 +1781,27 @@ } ] }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false }, "Uint64": { "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", @@ -1994,6 +2148,21 @@ } } }, + "check_royalties": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CheckRoyaltiesResponse", + "description": "Shows if the contract implements royalties if royalty_payments is true, marketplaces should pay them", + "type": "object", + "required": [ + "royalty_payments" + ], + "properties": { + "royalty_payments": { + "type": "boolean" + } + }, + "additionalProperties": false + }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "CollectionInfo_for_Empty", @@ -2405,17 +2574,17 @@ }, "nft_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NftInfoResponse_for_Cw2981QueryMsg", + "title": "NftInfoResponse_for_Nullable_Metadata", "type": "object", - "required": [ - "extension" - ], "properties": { "extension": { "description": "You can add any custom metadata here when you extend cw721-base", - "allOf": [ + "anyOf": [ { - "$ref": "#/definitions/Cw2981QueryMsg" + "$ref": "#/definitions/Metadata" + }, + { + "type": "null" } ] }, @@ -2429,53 +2598,106 @@ }, "additionalProperties": false, "definitions": { - "Cw2981QueryMsg": { - "oneOf": [ - { - "description": "Should be called on sale to see if royalties are owed by the marketplace selling the NFT, if CheckRoyalties returns true See https://eips.ethereum.org/EIPS/eip-2981", - "type": "object", - "required": [ - "royalty_info" + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" ], - "properties": { - "royalty_info": { - "type": "object", - "required": [ - "sale_price", - "token_id" - ], - "properties": { - "sale_price": { - "$ref": "#/definitions/Uint128" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false + "items": { + "$ref": "#/definitions/Trait" + } }, - { - "description": "Called against contract to determine if this NFT implements royalties. Should return a boolean as part of CheckRoyaltiesResponse - default can simply be true if royalties are implemented at token level (i.e. always check on sale)", - "type": "object", - "required": [ - "check_royalties" + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "royalty_payment_address": { + "description": "The payment address, may be different to or the same as the minter addr question: how do we validate this?", + "type": [ + "string", + "null" + ] + }, + "royalty_percentage": { + "description": "This is how much the minter takes as a cut when sold royalties are owed on this token if it is Some", + "type": [ + "integer", + "null" ], - "properties": { - "check_royalties": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false + "format": "uint64", + "minimum": 0.0 + }, + "youtube_url": { + "type": [ + "string", + "null" + ] } - ] + }, + "additionalProperties": false }, - "Uint128": { - "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", - "type": "string" + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false } } }, @@ -2821,6 +3043,30 @@ } } }, + "royalty_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RoyaltiesInfoResponse", + "type": "object", + "required": [ + "address", + "royalty_amount" + ], + "properties": { + "address": { + "type": "string" + }, + "royalty_amount": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, "tokens": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "TokensResponse", diff --git a/contracts/cw2981-royalties/src/lib.rs b/contracts/cw2981-royalties/src/lib.rs index 8a18cb4a7..30461eecf 100644 --- a/contracts/cw2981-royalties/src/lib.rs +++ b/contracts/cw2981-royalties/src/lib.rs @@ -12,7 +12,6 @@ pub use cw721_base::{ }; use crate::error::ContractError; -use crate::msg::Cw2981QueryMsg; // Version info for migration const CONTRACT_NAME: &str = "crates.io:cw2981-royalties"; @@ -51,20 +50,15 @@ pub type Extension = Option; pub type MintExtension = Option; -pub type Cw2981Contract<'a> = Cw721Contract< - 'a, - Extension, - Empty, - Empty, - Cw2981QueryMsg, - DefaultOptionCollectionInfoExtension, ->; +pub type Cw2981Contract<'a> = + Cw721Contract<'a, Extension, Empty, Empty, DefaultOptionCollectionInfoExtension>; pub type ExecuteMsg = cw721_base::msg::ExecuteMsg; -pub type QueryMsg = cw721_base::msg::QueryMsg; #[cfg(not(feature = "library"))] pub mod entry { + use self::msg::QueryMsg; + use super::*; use cosmwasm_std::entry_point; @@ -118,14 +112,12 @@ pub mod entry { #[entry_point] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Extension { msg } => match msg { - Cw2981QueryMsg::RoyaltyInfo { - token_id, - sale_price, - } => to_json_binary(&query_royalties_info(deps, env, token_id, sale_price)?), - Cw2981QueryMsg::CheckRoyalties {} => to_json_binary(&check_royalties(deps)?), - }, - _ => Cw2981Contract::default().query(deps, env, msg), + QueryMsg::RoyaltyInfo { + token_id, + sale_price, + } => to_json_binary(&query_royalties_info(deps, env, token_id, sale_price)?), + QueryMsg::CheckRoyalties {} => to_json_binary(&check_royalties(deps)?), + _ => Cw2981Contract::default().query(deps, env, msg.into()), } } } @@ -133,7 +125,7 @@ pub mod entry { #[cfg(test)] mod tests { use super::*; - use crate::msg::{CheckRoyaltiesResponse, RoyaltiesInfoResponse}; + use crate::msg::{CheckRoyaltiesResponse, QueryMsg, RoyaltiesInfoResponse}; use cosmwasm_std::{from_json, Uint128}; @@ -249,9 +241,7 @@ mod tests { assert_eq!(res, expected); // also check the longhand way - let query_msg = QueryMsg::Extension { - msg: Cw2981QueryMsg::CheckRoyalties {}, - }; + let query_msg = QueryMsg::CheckRoyalties {}; let query_res: CheckRoyaltiesResponse = from_json(entry::query(deps.as_ref(), mock_env(), query_msg).unwrap()).unwrap(); assert_eq!(query_res, expected); @@ -303,11 +293,9 @@ mod tests { assert_eq!(res, expected); // also check the longhand way - let query_msg = QueryMsg::Extension { - msg: Cw2981QueryMsg::RoyaltyInfo { - token_id: token_id.to_string(), - sale_price: Uint128::new(100), - }, + let query_msg = QueryMsg::RoyaltyInfo { + token_id: token_id.to_string(), + sale_price: Uint128::new(100), }; let query_res: RoyaltiesInfoResponse = from_json(entry::query(deps.as_ref(), mock_env(), query_msg).unwrap()).unwrap(); diff --git a/contracts/cw2981-royalties/src/msg.rs b/contracts/cw2981-royalties/src/msg.rs index fa9c7f387..468fb3fd0 100644 --- a/contracts/cw2981-royalties/src/msg.rs +++ b/contracts/cw2981-royalties/src/msg.rs @@ -1,9 +1,20 @@ +use crate::Extension; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{CustomMsg, Uint128}; +use cosmwasm_std::{Addr, Empty, Uint128}; +use cw721::{msg::Cw721QueryMsg, state::DefaultOptionCollectionInfoExtension}; +use cw721_base::{ + msg::MinterResponse, + query::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, + NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + }, + state::CollectionInfo, +}; +use cw_ownable::Ownership; #[cw_serde] #[derive(QueryResponses)] -pub enum Cw2981QueryMsg { +pub enum QueryMsg { /// Should be called on sale to see if royalties are owed /// by the marketplace selling the NFT, if CheckRoyalties /// returns true @@ -24,16 +35,199 @@ pub enum Cw2981QueryMsg { /// (i.e. always check on sale) #[returns(CheckRoyaltiesResponse)] CheckRoyalties {}, + + // -- below copied from Cw721QueryMsg + /// Return the owner of the given token, error if token does not exist + #[returns(OwnerOfResponse)] + OwnerOf { + token_id: String, + /// unset or false will filter out expired approvals, you must set to true to see them + include_expired: Option, + }, + /// Return operator that can access all of the owner's tokens. + #[returns(ApprovalResponse)] + Approval { + token_id: String, + spender: String, + include_expired: Option, + }, + /// Return approvals that a token has + #[returns(ApprovalsResponse)] + Approvals { + token_id: String, + include_expired: Option, + }, + /// Return approval of a given operator for all tokens of an owner, error if not set + #[returns(OperatorResponse)] + Operator { + owner: String, + operator: String, + include_expired: Option, + }, + /// List all operators that can access all of the owner's tokens + #[returns(OperatorsResponse)] + AllOperators { + owner: String, + /// unset or false will filter out expired items, you must set to true to see them + include_expired: Option, + start_after: Option, + limit: Option, + }, + /// Total number of tokens issued + #[returns(NumTokensResponse)] + NumTokens {}, + + #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] + #[returns(CollectionInfo)] + ContractInfo {}, + + /// With MetaData Extension. + /// Returns top-level metadata about the contract + #[returns(CollectionInfo)] + GetCollectionInfo {}, + + #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] + #[returns(Ownership)] + Ownership {}, + + #[returns(Ownership)] + GetMinterOwnership {}, + + #[returns(Ownership)] + GetCreatorOwnership {}, + + /// With MetaData Extension. + /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* + /// but directly from the contract + #[returns(NftInfoResponse)] + NftInfo { token_id: String }, + /// With MetaData Extension. + /// Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization + /// for clients + #[returns(AllNftInfoResponse)] + AllNftInfo { + token_id: String, + /// unset or false will filter out expired approvals, you must set to true to see them + include_expired: Option, + }, + + /// With Enumerable extension. + /// Returns all tokens owned by the given address, [] if unset. + #[returns(TokensResponse)] + Tokens { + owner: String, + start_after: Option, + limit: Option, + }, + /// With Enumerable extension. + /// Requires pagination. Lists all token_ids controlled by the contract. + #[returns(TokensResponse)] + AllTokens { + start_after: Option, + limit: Option, + }, + + /// Return the minter + #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] + #[returns(MinterResponse)] + Minter {}, + + #[returns(Option)] + GetWithdrawAddress {}, + + // -- below queries, Extension and GetCollectionInfoExtension, are just dummies, since type annotations are required for + // -- TMetadataExtension and TCollectionInfoExtension, Error: + // -- "type annotations needed: cannot infer type for type parameter `TMetadataExtension` declared on the enum `Cw721QueryMsg`" + /// Do not use - dummy extension query, needed for inferring type parameter during compile + #[returns(())] + Extension { msg: Extension }, + + /// Do not use - dummy collection info extension query, needed for inferring type parameter during compile + #[returns(())] + GetCollectionInfoExtension { + msg: DefaultOptionCollectionInfoExtension, + }, } -impl Default for Cw2981QueryMsg { - fn default() -> Self { - Cw2981QueryMsg::CheckRoyalties {} +// impl Default for Cw2981QueryMsg { +// fn default() -> Self { +// Cw2981QueryMsg::CheckRoyalties {} +// } +// } + +// impl CustomMsg for Cw2981QueryMsg {} + +impl From for Cw721QueryMsg { + fn from(msg: QueryMsg) -> Cw721QueryMsg { + match msg { + QueryMsg::OwnerOf { + token_id, + include_expired, + } => Cw721QueryMsg::OwnerOf { + token_id, + include_expired, + }, + QueryMsg::NumTokens {} => Cw721QueryMsg::NumTokens {}, + #[allow(deprecated)] + QueryMsg::ContractInfo {} => Cw721QueryMsg::GetCollectionInfo {}, + QueryMsg::GetCollectionInfo {} => Cw721QueryMsg::GetCollectionInfo {}, + QueryMsg::NftInfo { token_id } => Cw721QueryMsg::NftInfo { token_id }, + QueryMsg::AllNftInfo { + token_id, + include_expired, + } => Cw721QueryMsg::AllNftInfo { + token_id, + include_expired, + }, + QueryMsg::Tokens { + owner, + start_after, + limit, + } => Cw721QueryMsg::Tokens { + owner, + start_after, + limit, + }, + QueryMsg::AllTokens { start_after, limit } => { + Cw721QueryMsg::AllTokens { start_after, limit } + } + #[allow(deprecated)] + QueryMsg::Minter {} => Cw721QueryMsg::Minter {}, + QueryMsg::GetMinterOwnership {} => Cw721QueryMsg::GetMinterOwnership {}, + QueryMsg::GetCreatorOwnership {} => Cw721QueryMsg::GetCreatorOwnership {}, + QueryMsg::GetWithdrawAddress {} => Cw721QueryMsg::GetWithdrawAddress {}, + QueryMsg::AllOperators { + owner, + include_expired, + start_after, + limit, + } => Cw721QueryMsg::AllOperators { + owner, + include_expired, + start_after, + limit, + }, + QueryMsg::Approval { + token_id, + spender, + include_expired, + } => Cw721QueryMsg::Approval { + token_id, + spender, + include_expired, + }, + QueryMsg::Approvals { + token_id, + include_expired, + } => Cw721QueryMsg::Approvals { + token_id, + include_expired, + }, + msg => unreachable!("Unsupported query: {:?}", msg), + } } } -impl CustomMsg for Cw2981QueryMsg {} - #[cw_serde] pub struct RoyaltiesInfoResponse { pub address: String, diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 406daba26..06254c37d 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -6,34 +6,19 @@ use serde::Serialize; use crate::Cw721Contract; -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > - Cw721Execute< - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> + Cw721Execute for Cw721Contract< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index 3dc4162e8..450327fcd 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -47,7 +47,6 @@ pub mod entry { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.instantiate(deps, env, info, msg, CONTRACT_NAME, CONTRACT_VERSION) @@ -68,7 +67,6 @@ pub mod entry { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.execute(deps, env, info, msg) @@ -78,13 +76,12 @@ pub mod entry { pub fn query( deps: Deps, env: Env, - msg: Cw721QueryMsg, + msg: Cw721QueryMsg, ) -> StdResult { let contract = Cw721Contract::< DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.query(deps, env, msg) @@ -100,7 +97,6 @@ pub mod entry { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.migrate(deps, env, msg, CONTRACT_NAME, CONTRACT_VERSION) diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index 98dbc236e..1b900e410 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -6,27 +6,19 @@ use serde::Serialize; use crate::Cw721Contract; -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > Cw721Query +impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> + Cw721Query for Cw721Contract< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index a05bf8987..be7fea632 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -6,46 +6,35 @@ use serde::Serialize; pub struct Cw721Contract< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, - TMetadataResponse: CustomMsg, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub config: Cw721Config< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >, } -impl< - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > Default +impl Default for Cw721Contract< 'static, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { diff --git a/contracts/cw721-expiration/src/contract_tests.rs b/contracts/cw721-expiration/src/contract_tests.rs index 303b4abb6..0cde65959 100644 --- a/contracts/cw721-expiration/src/contract_tests.rs +++ b/contracts/cw721-expiration/src/contract_tests.rs @@ -35,14 +35,12 @@ fn setup_contract( DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, > { let contract = Cw721ExpirationContract::< DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); let msg = InstantiateMsg { @@ -67,7 +65,6 @@ fn proper_instantiation() { DefaultOptionMetadataExtension, Empty, Empty, - Empty, Option, >::default(); @@ -136,7 +133,6 @@ fn proper_instantiation_with_collection_info() { DefaultOptionMetadataExtension, Empty, Empty, - Empty, Option, >::default(); diff --git a/contracts/cw721-expiration/src/execute.rs b/contracts/cw721-expiration/src/execute.rs index fd4e38004..ec3640d51 100644 --- a/contracts/cw721-expiration/src/execute.rs +++ b/contracts/cw721-expiration/src/execute.rs @@ -12,27 +12,18 @@ use crate::{ CONTRACT_VERSION, }; -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> Cw721ExpirationContract< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { // -- instantiate -- @@ -47,10 +38,9 @@ where return Err(ContractError::MinExpiration {}); } let contract = Cw721ExpirationContract::< - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >::default(); contract @@ -79,13 +69,12 @@ where deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721ExecuteMsg, + msg: Cw721ExecuteMsg, ) -> Result, ContractError> { let contract = Cw721ExpirationContract::< - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >::default(); match msg { @@ -133,7 +122,7 @@ where token_id: String, owner: String, token_uri: Option, - extension: TMetadata, + extension: TMetadataExtension, ) -> Result, ContractError> { let mint_timstamp = env.block.time; self.mint_timestamps diff --git a/contracts/cw721-expiration/src/lib.rs b/contracts/cw721-expiration/src/lib.rs index d225ee32d..93cc11ab2 100644 --- a/contracts/cw721-expiration/src/lib.rs +++ b/contracts/cw721-expiration/src/lib.rs @@ -47,7 +47,6 @@ pub mod entry { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.instantiate(deps, env, info, msg) @@ -68,7 +67,6 @@ pub mod entry { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.execute(deps, env, info, msg) @@ -78,13 +76,12 @@ pub mod entry { pub fn query( deps: Deps, env: Env, - msg: QueryMsg, + msg: QueryMsg, ) -> Result { let contract = Cw721ExpirationContract::< DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.query(deps, env, msg) @@ -161,7 +158,6 @@ mod tests { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default() .expiration_days diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index 37aa56816..65683d509 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -3,7 +3,6 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::Addr; use cw721::state::CollectionInfo; use cw_ownable::Ownership; -use schemars::JsonSchema; #[cw_serde] pub struct InstantiateMsg { @@ -31,7 +30,7 @@ pub struct InstantiateMsg { #[cw_serde] #[derive(QueryResponses)] -pub enum QueryMsg { +pub enum QueryMsg { // -------- below adds `include_expired_nft` prop to cw721/src/msg.rs -------- /// Return the owner of the given token, error if token does not exist #[returns(cw721::query::OwnerOfResponse)] @@ -150,7 +149,7 @@ pub enum QueryMsg { /// Extension query #[returns(())] - Extension { msg: TMetadataResponse }, + Extension { msg: TMetadataExtension }, /// This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: /// `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg` diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index 39e98ef65..ef101173d 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -8,40 +8,30 @@ use serde::Serialize; use crate::{error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract}; -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> Cw721ExpirationContract< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub fn query( &self, deps: Deps, env: Env, - msg: QueryMsg, + msg: QueryMsg, ) -> Result { let contract = Cw721ExpirationContract::< - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >::default(); match msg { @@ -214,7 +204,7 @@ where env: Env, token_id: String, include_expired_nft: bool, - ) -> Result, ContractError> { + ) -> Result, ContractError> { if !include_expired_nft { self.assert_nft_expired(deps, &env, token_id.as_str())?; } @@ -333,7 +323,7 @@ where token_id: String, include_expired_approval: bool, include_expired_nft: bool, - ) -> Result, ContractError> { + ) -> Result, ContractError> { if !include_expired_nft { self.assert_nft_expired(deps, &env, token_id.as_str())?; } diff --git a/contracts/cw721-expiration/src/state.rs b/contracts/cw721-expiration/src/state.rs index 0cb4f3b2f..2c626f746 100644 --- a/contracts/cw721-expiration/src/state.rs +++ b/contracts/cw721-expiration/src/state.rs @@ -6,14 +6,12 @@ use serde::Serialize; pub struct Cw721ExpirationContract< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, - TMetadataResponse: CustomMsg, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { @@ -21,33 +19,24 @@ pub struct Cw721ExpirationContract< pub mint_timestamps: Map<'a, &'a str, Timestamp>, pub base_contract: Cw721Contract< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >, } -impl< - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > Default +impl Default for Cw721ExpirationContract< 'static, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { diff --git a/contracts/cw721-fixed-price/src/contract.rs b/contracts/cw721-fixed-price/src/contract.rs index a365e7833..558ece937 100644 --- a/contracts/cw721-fixed-price/src/contract.rs +++ b/contracts/cw721-fixed-price/src/contract.rs @@ -13,7 +13,7 @@ use cw2::set_contract_version; use cw20::Cw20ReceiveMsg; use cw721::helpers::Cw721Contract; use cw721::msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}; -use cw721::state::DefaultOptionCollectionInfoExtension; +use cw721::state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}; use cw_utils::parse_reply_instantiate_data; // version info for migration info @@ -161,7 +161,7 @@ pub fn execute_receive( return Err(ContractError::WrongPaymentAmount {}); } - let mint_msg = Cw721ExecuteMsg::<_, Empty, Empty>::Mint { + let mint_msg = Cw721ExecuteMsg::::Mint { token_id: config.unused_token_id.to_string(), owner: sender, token_uri: config.token_uri.clone().into(), @@ -170,9 +170,13 @@ pub fn execute_receive( match config.cw721_address.clone() { Some(cw721) => { - let callback = - Cw721Contract::(cw721, PhantomData, PhantomData, PhantomData) - .call(mint_msg)?; + let callback = Cw721Contract::( + cw721, + PhantomData, + PhantomData, + PhantomData, + ) + .call(mint_msg)?; config.unused_token_id += 1; CONFIG.save(deps.storage, &config)?; diff --git a/contracts/cw721-non-transferable/src/lib.rs b/contracts/cw721-non-transferable/src/lib.rs index a4d672ce1..da684ff1c 100644 --- a/contracts/cw721-non-transferable/src/lib.rs +++ b/contracts/cw721-non-transferable/src/lib.rs @@ -19,7 +19,6 @@ pub type Cw721NonTransferableContract<'a> = Cw721Contract< DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >; diff --git a/contracts/cw721-non-transferable/src/msg.rs b/contracts/cw721-non-transferable/src/msg.rs index 6b921d9cb..59c461997 100644 --- a/contracts/cw721-non-transferable/src/msg.rs +++ b/contracts/cw721-non-transferable/src/msg.rs @@ -1,6 +1,8 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::Empty; -use cw721::{msg::Cw721QueryMsg, state::DefaultOptionCollectionInfoExtension}; +use cw721::{ + msg::Cw721QueryMsg, + state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, +}; #[cw_serde] pub struct InstantiateMsg { @@ -16,6 +18,8 @@ pub struct InstantiateMsg { #[cw_serde] pub enum QueryMsg { Admin {}, + + // -- below copied from Cw721QueryMsg OwnerOf { token_id: String, include_expired: Option, @@ -64,8 +68,12 @@ pub enum QueryMsg { GetWithdrawAddress {}, } -impl From for Cw721QueryMsg { - fn from(msg: QueryMsg) -> Cw721QueryMsg { +impl From + for Cw721QueryMsg +{ + fn from( + msg: QueryMsg, + ) -> Cw721QueryMsg { match msg { QueryMsg::OwnerOf { token_id, diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index 44b36df00..fc445aed1 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -20,16 +20,14 @@ use crate::{ }; pub trait Cw721Execute< - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn instantiate( @@ -42,7 +40,7 @@ pub trait Cw721Execute< contract_version: &str, ) -> Result, Cw721ContractError> { cw2::set_contract_version(deps.storage, contract_name, contract_version)?; - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let collection_info = CollectionInfo { name: msg.name, symbol: msg.symbol, @@ -82,7 +80,7 @@ pub trait Cw721Execute< deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721ExecuteMsg, + msg: Cw721ExecuteMsg, ) -> Result, Cw721ContractError> { match msg { Cw721ExecuteMsg::UpdateCollectionInfo { collection_info } => { @@ -167,7 +165,7 @@ pub trait Cw721Execute< recipient: String, token_id: String, ) -> Result, Cw721ContractError> { - _transfer_nft::(deps, &env, &info, &recipient, &token_id)?; + _transfer_nft::(deps, &env, &info, &recipient, &token_id)?; Ok(Response::new() .add_attribute("action", "transfer_nft") @@ -186,7 +184,7 @@ pub trait Cw721Execute< msg: Binary, ) -> Result, Cw721ContractError> { // Transfer token - _transfer_nft::(deps, &env, &info, &contract, &token_id)?; + _transfer_nft::(deps, &env, &info, &contract, &token_id)?; let send = Cw721ReceiveMsg { sender: info.sender.to_string(), @@ -212,7 +210,7 @@ pub trait Cw721Execute< token_id: String, expires: Option, ) -> Result, Cw721ContractError> { - _update_approvals::(deps, &env, &info, &spender, &token_id, true, expires)?; + _update_approvals::(deps, &env, &info, &spender, &token_id, true, expires)?; Ok(Response::new() .add_attribute("action", "approve") @@ -229,7 +227,7 @@ pub trait Cw721Execute< spender: String, token_id: String, ) -> Result, Cw721ContractError> { - _update_approvals::(deps, &env, &info, &spender, &token_id, false, None)?; + _update_approvals::(deps, &env, &info, &spender, &token_id, false, None)?; Ok(Response::new() .add_attribute("action", "revoke") @@ -255,10 +253,9 @@ pub trait Cw721Execute< // set the operator for us let operator_addr = deps.api.addr_validate(&operator)?; let config = Cw721Config::< - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >::default(); config @@ -282,10 +279,9 @@ pub trait Cw721Execute< ) -> Result, Cw721ContractError> { let operator_addr = deps.api.addr_validate(&operator)?; let config = Cw721Config::< - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >::default(); config @@ -306,10 +302,9 @@ pub trait Cw721Execute< token_id: String, ) -> Result, Cw721ContractError> { let config = Cw721Config::< - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >::default(); let token = config.nft_info.load(deps.storage, &token_id)?; @@ -357,13 +352,9 @@ pub trait Cw721Execute< extension: msg.extension, updated_at: env.block.time, }; - let config = Cw721Config::< - Empty, - TCustomResponseMessage, - Empty, - Empty, - TCollectionInfoExtension, - >::default(); + let config = + Cw721Config::::default( + ); config .collection_info .save(deps.storage, &collection_info)?; @@ -380,7 +371,7 @@ pub trait Cw721Execute< token_id: String, owner: String, token_uri: Option, - extension: TMetadata, + extension: TMetadataExtension, ) -> Result, Cw721ContractError> { MINTER.assert_owner(deps.storage, &info.sender)?; @@ -391,7 +382,7 @@ pub trait Cw721Execute< token_uri, extension, }; - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); config .nft_info .update(deps.storage, &token_id, |old| match old { @@ -443,10 +434,9 @@ pub trait Cw721Execute< CREATOR.assert_owner(deps.storage, sender)?; deps.api.addr_validate(&address)?; let config = Cw721Config::< - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >::default(); config.withdraw_address.save(deps.storage, &address)?; @@ -462,10 +452,9 @@ pub trait Cw721Execute< ) -> Result, Cw721ContractError> { CREATOR.assert_owner(storage, sender)?; let config = Cw721Config::< - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >::default(); let address = config.withdraw_address.may_load(storage)?; @@ -486,10 +475,9 @@ pub trait Cw721Execute< amount: &Coin, ) -> Result, Cw721ContractError> { let withdraw_address = Cw721Config::< - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >::default() .withdraw_address @@ -512,17 +500,17 @@ pub trait Cw721Execute< } // ------- helper cw721 functions ------- -fn _transfer_nft( +fn _transfer_nft( deps: DepsMut, env: &Env, info: &MessageInfo, recipient: &str, token_id: &str, -) -> Result, Cw721ContractError> +) -> Result, Cw721ContractError> where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, { - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let mut token = config.nft_info.load(deps.storage, token_id)?; // ensure we have permissions check_can_send(deps.as_ref(), env, info, &token)?; @@ -534,7 +522,7 @@ where } #[allow(clippy::too_many_arguments)] -fn _update_approvals( +fn _update_approvals( deps: DepsMut, env: &Env, info: &MessageInfo, @@ -543,11 +531,11 @@ fn _update_approvals( // if add == false, remove. if add == true, remove then set with this expiration add: bool, expires: Option, -) -> Result, Cw721ContractError> +) -> Result, Cw721ContractError> where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, { - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let mut token = config.nft_info.load(deps.storage, token_id)?; // ensure we have permissions check_can_approve(deps.as_ref(), env, info, &token)?; @@ -576,21 +564,21 @@ where } /// returns true if the sender can execute approve or reject on the contract -pub fn check_can_approve( +pub fn check_can_approve( deps: Deps, env: &Env, info: &MessageInfo, - token: &NftInfo, + token: &NftInfo, ) -> Result<(), Cw721ContractError> where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, { // owner can approve if token.owner == info.sender { return Ok(()); } // operator can approve - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let op = config .operators .may_load(deps.storage, (&token.owner, &info.sender))?; @@ -607,11 +595,11 @@ where } /// returns true iff the sender can transfer ownership of the token -pub fn check_can_send( +pub fn check_can_send( deps: Deps, env: &Env, info: &MessageInfo, - token: &NftInfo, + token: &NftInfo, ) -> Result<(), Cw721ContractError> { // owner can send if token.owner == info.sender { @@ -628,7 +616,7 @@ pub fn check_can_send( } // operator can send - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let op = config .operators // has token owner approved/gave grant to sender for full control over owner's NFTs? @@ -752,7 +740,6 @@ pub fn migrate_legacy_collection_info( DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); match contract.collection_info.may_load(storage)? { diff --git a/packages/cw721/src/helpers.rs b/packages/cw721/src/helpers.rs index 17fbc145b..f1d8f8ada 100644 --- a/packages/cw721/src/helpers.rs +++ b/packages/cw721/src/helpers.rs @@ -16,30 +16,27 @@ use serde::de::DeserializeOwned; use serde::Serialize; #[cw_serde] -pub struct Cw721Contract< - TMetadataResponse: CustomMsg, - TExtensionExecuteMsg: CustomMsg, - TCollectionInfoExtension, ->( +pub struct Cw721Contract( pub Addr, - pub PhantomData, + pub PhantomData, pub PhantomData, pub PhantomData, ); #[allow(dead_code)] -impl - Cw721Contract +impl + Cw721Contract where + TMetadataExtension: Serialize + DeserializeOwned + Clone, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub fn addr(&self) -> Addr { self.0.clone() } - pub fn call( + pub fn call( &self, - msg: Cw721ExecuteMsg, + msg: Cw721ExecuteMsg, ) -> StdResult { let msg = to_json_binary(&msg)?; Ok(WasmMsg::Execute { @@ -53,7 +50,7 @@ where pub fn query( &self, querier: &QuerierWrapper, - req: Cw721QueryMsg, + req: Cw721QueryMsg, ) -> StdResult { let query = WasmQuery::Smart { contract_addr: self.addr().into(), diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index 1c9d5c42a..32d4fa97f 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -2,7 +2,6 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, Binary, Coin}; use cw_ownable::{Action, Ownership}; use cw_utils::Expiration; -use schemars::JsonSchema; use crate::query::{ AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, NumTokensResponse, @@ -14,7 +13,7 @@ use crate::state::CollectionInfo; use cosmwasm_std::Empty; #[cw_serde] -pub enum Cw721ExecuteMsg { +pub enum Cw721ExecuteMsg { #[deprecated(since = "0.19.0", note = "Please use UpdateMinterOwnership instead")] UpdateOwnership(Action), UpdateMinterOwnership(Action), @@ -70,7 +69,7 @@ pub enum Cw721ExecuteMsg, /// Any custom extension used by this contract - extension: TMetadata, + extension: TMetadataExtension, }, /// Burn an NFT the sender has access to @@ -118,7 +117,7 @@ pub struct Cw721InstantiateMsg { #[cw_serde] #[derive(QueryResponses)] -pub enum Cw721QueryMsg { +pub enum Cw721QueryMsg { /// Return the owner of the given token, error if token does not exist #[returns(OwnerOfResponse)] OwnerOf { @@ -181,12 +180,12 @@ pub enum Cw721QueryMsg /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract - #[returns(NftInfoResponse)] + #[returns(NftInfoResponse)] NftInfo { token_id: String }, /// With MetaData Extension. /// Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization /// for clients - #[returns(AllNftInfoResponse)] + #[returns(AllNftInfoResponse)] AllNftInfo { token_id: String, /// unset or false will filter out expired approvals, you must set to true to see them @@ -218,11 +217,11 @@ pub enum Cw721QueryMsg GetWithdrawAddress {}, // -- below queries, Extension and GetCollectionInfoExtension, are just dummies, since type annotations are required for - // -- TMetadataResponse and TCollectionInfoExtension, Error: - // -- "type annotations needed: cannot infer type for type parameter `TMetadataResponse` declared on the enum `Cw721QueryMsg`" + // -- TMetadataExtension and TCollectionInfoExtension, Error: + // -- "type annotations needed: cannot infer type for type parameter `TMetadataExtension` declared on the enum `Cw721QueryMsg`" /// Do not use - dummy extension query, needed for inferring type parameter during compile #[returns(())] - Extension { msg: TMetadataResponse }, + Extension { msg: TMetadataExtension }, /// Do not use - dummy collection info extension query, needed for inferring type parameter during compile #[returns(())] diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index 85deb27bc..0c9503bc6 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -1,7 +1,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - to_json_binary, Addr, Binary, BlockInfo, CustomMsg, Deps, Empty, Env, Order, StdError, - StdResult, Storage, + to_json_binary, Addr, Binary, BlockInfo, Deps, Empty, Env, Order, StdError, StdResult, Storage, }; use cw_ownable::Ownership; use cw_storage_plus::Bound; @@ -17,17 +16,16 @@ use crate::{ pub const DEFAULT_LIMIT: u32 = 10; pub const MAX_LIMIT: u32 = 1000; -pub trait Cw721Query +pub trait Cw721Query where - TMetadata: Serialize + DeserializeOwned + Clone, - TMetadataResponse: CustomMsg, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn query( &self, deps: Deps, env: Env, - msg: Cw721QueryMsg, + msg: Cw721QueryMsg, ) -> StdResult { match msg { #[allow(deprecated)] @@ -158,20 +156,14 @@ where deps: Deps, _env: Env, ) -> StdResult> { - Cw721Config::::default() + Cw721Config::::default() .collection_info .load(deps.storage) } fn query_num_tokens(&self, deps: Deps, _env: Env) -> StdResult { - let count = Cw721Config::< - TMetadata, - Empty, - Empty, - TMetadataResponse, - TCollectionInfoExtension, - >::default() - .token_count(deps.storage)?; + let count = Cw721Config::::default() + .token_count(deps.storage)?; Ok(NumTokensResponse { count }) } @@ -180,16 +172,10 @@ where deps: Deps, _env: Env, token_id: String, - ) -> StdResult> { - let info = Cw721Config::< - TMetadata, - Empty, - Empty, - TMetadataResponse, - TCollectionInfoExtension, - >::default() - .nft_info - .load(deps.storage, &token_id)?; + ) -> StdResult> { + let info = Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; Ok(NftInfoResponse { token_uri: info.token_uri, extension: info.extension, @@ -203,15 +189,9 @@ where token_id: String, include_expired_approval: bool, ) -> StdResult { - let nft_info = Cw721Config::< - TMetadata, - Empty, - Empty, - TMetadataResponse, - TCollectionInfoExtension, - >::default() - .nft_info - .load(deps.storage, &token_id)?; + let nft_info = Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; Ok(OwnerOfResponse { owner: nft_info.owner.to_string(), approvals: humanize_approvals(&env.block, &nft_info, include_expired_approval), @@ -230,15 +210,9 @@ where let owner_addr = deps.api.addr_validate(&owner)?; let operator_addr = deps.api.addr_validate(&operator)?; - let info = Cw721Config::< - TMetadata, - Empty, - Empty, - TMetadataResponse, - TCollectionInfoExtension, - >::default() - .operators - .may_load(deps.storage, (&owner_addr, &operator_addr))?; + let info = Cw721Config::::default() + .operators + .may_load(deps.storage, (&owner_addr, &operator_addr))?; if let Some(expires) = info { if !include_expired_approval && expires.is_expired(&env.block) { @@ -271,22 +245,19 @@ where let start = start_addr.as_ref().map(Bound::exclusive); let owner_addr = deps.api.addr_validate(&owner)?; - let res: StdResult> = Cw721Config::< - TMetadata, - Empty, - Empty, - TMetadataResponse, - TCollectionInfoExtension, - >::default() - .operators - .prefix(&owner_addr) - .range(deps.storage, start, None, Order::Ascending) - .filter(|r| { - include_expired_approval || r.is_err() || !r.as_ref().unwrap().1.is_expired(&env.block) - }) - .take(limit) - .map(parse_approval) - .collect(); + let res: StdResult> = + Cw721Config::::default() + .operators + .prefix(&owner_addr) + .range(deps.storage, start, None, Order::Ascending) + .filter(|r| { + include_expired_approval + || r.is_err() + || !r.as_ref().unwrap().1.is_expired(&env.block) + }) + .take(limit) + .map(parse_approval) + .collect(); Ok(OperatorsResponse { operators: res? }) } @@ -298,15 +269,9 @@ where spender: String, include_expired_approval: bool, ) -> StdResult { - let token = Cw721Config::< - TMetadata, - Empty, - Empty, - TMetadataResponse, - TCollectionInfoExtension, - >::default() - .nft_info - .load(deps.storage, &token_id)?; + let token = Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; // token owner has absolute approval if token.owner == spender { @@ -345,15 +310,9 @@ where token_id: String, include_expired_approval: bool, ) -> StdResult { - let token = Cw721Config::< - TMetadata, - Empty, - Empty, - TMetadataResponse, - TCollectionInfoExtension, - >::default() - .nft_info - .load(deps.storage, &token_id)?; + let token = Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; let approvals: Vec<_> = token .approvals .into_iter() @@ -379,20 +338,15 @@ where let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); let owner_addr = deps.api.addr_validate(&owner)?; - let tokens: Vec = Cw721Config::< - TMetadata, - Empty, - Empty, - TMetadataResponse, - TCollectionInfoExtension, - >::default() - .nft_info - .idx - .owner - .prefix(owner_addr) - .keys(deps.storage, start, None, Order::Ascending) - .take(limit) - .collect::>>()?; + let tokens: Vec = + Cw721Config::::default() + .nft_info + .idx + .owner + .prefix(owner_addr) + .keys(deps.storage, start, None, Order::Ascending) + .take(limit) + .collect::>>()?; Ok(TokensResponse { tokens }) } @@ -407,18 +361,13 @@ where let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); - let tokens: StdResult> = Cw721Config::< - TMetadata, - Empty, - Empty, - TMetadataResponse, - TCollectionInfoExtension, - >::default() - .nft_info - .range(deps.storage, start, None, Order::Ascending) - .take(limit) - .map(|item| item.map(|(k, _)| k)) - .collect(); + let tokens: StdResult> = + Cw721Config::::default() + .nft_info + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|item| item.map(|(k, _)| k)) + .collect(); Ok(TokensResponse { tokens: tokens? }) } @@ -429,16 +378,10 @@ where env: Env, token_id: String, include_expired_approval: bool, - ) -> StdResult> { - let nft_info = Cw721Config::< - TMetadata, - Empty, - Empty, - TMetadataResponse, - TCollectionInfoExtension, - >::default() - .nft_info - .load(deps.storage, &token_id)?; + ) -> StdResult> { + let nft_info = Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; Ok(AllNftInfoResponse { access: OwnerOfResponse { owner: nft_info.owner.to_string(), @@ -451,12 +394,7 @@ where }) } - fn query_extension( - &self, - _deps: Deps, - _env: Env, - _msg: TMetadataResponse, - ) -> StdResult { + fn query_extension(&self, _deps: Deps, _env: Env, _msg: TMetadataExtension) -> StdResult { Ok(Binary::default()) } @@ -470,7 +408,7 @@ where } fn query_withdraw_address(&self, deps: Deps) -> StdResult> { - Cw721Config::::default() + Cw721Config::::default() .withdraw_address .may_load(deps.storage) } @@ -480,13 +418,13 @@ pub fn parse_approval(item: StdResult<(Addr, Expiration)>) -> StdResult( +pub fn humanize_approvals( block: &BlockInfo, - nft_info: &NftInfo, + nft_info: &NftInfo, include_expired_approval: bool, ) -> Vec where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, { nft_info .approvals @@ -537,21 +475,21 @@ pub struct NumTokensResponse { } #[cw_serde] -pub struct NftInfoResponse { +pub struct NftInfoResponse { /// Universal resource identifier for this NFT /// Should point to a JSON file that conforms to the ERC721 /// Metadata JSON Schema pub token_uri: Option, /// You can add any custom metadata here when you extend cw721-base - pub extension: TMetadata, + pub extension: TMetadataExtension, } #[cw_serde] -pub struct AllNftInfoResponse { +pub struct AllNftInfoResponse { /// Who can transfer the token pub access: OwnerOfResponse, /// Data on the token itself, - pub info: NftInfoResponse, + pub info: NftInfoResponse, } #[cw_serde] diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index c2fd0a260..84840d51b 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -22,14 +22,12 @@ pub type DefaultOptionMetadataExtension = Option; pub struct Cw721Config< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, - TMetadataResponse: CustomMsg, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { @@ -39,33 +37,24 @@ pub struct Cw721Config< /// Stored as (granter, operator) giving operator full control over granter's account. /// NOTE: granter is the owner, so operator has only control for NFTs owned by granter! pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, - pub nft_info: IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, TMetadata>>, + pub nft_info: IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, TMetadataExtension>>, pub withdraw_address: Item<'a, String>, pub(crate) _custom_response: PhantomData, - pub(crate) _custom_query: PhantomData, pub(crate) _custom_execute: PhantomData, } -impl< - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > Default +impl Default for Cw721Config< 'static, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { @@ -80,26 +69,17 @@ where } } -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> Cw721Config< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn new( @@ -121,7 +101,6 @@ where withdraw_address: Item::new(withdraw_address_key), _custom_response: PhantomData, _custom_execute: PhantomData, - _custom_query: PhantomData, } } @@ -142,12 +121,12 @@ where } } -pub fn token_owner_idx(_pk: &[u8], d: &NftInfo) -> Addr { +pub fn token_owner_idx(_pk: &[u8], d: &NftInfo) -> Addr { d.owner.clone() } #[cw_serde] -pub struct NftInfo { +pub struct NftInfo { /// The owner of the newly minted NFT pub owner: Addr, /// Approvals are stored here, as we clear them all upon transfer and cannot accumulate much @@ -159,7 +138,7 @@ pub struct NftInfo { pub token_uri: Option, /// You can add any custom metadata here when you extend cw721-base - pub extension: TMetadata, + pub extension: TMetadataExtension, } #[cw_serde] @@ -176,19 +155,19 @@ impl Approval { } } -pub struct TokenIndexes<'a, TMetadata> +pub struct TokenIndexes<'a, TMetadataExtension> where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, { - pub owner: MultiIndex<'a, Addr, NftInfo, String>, + pub owner: MultiIndex<'a, Addr, NftInfo, String>, } -impl<'a, TMetadata> IndexList> for TokenIndexes<'a, TMetadata> +impl<'a, TMetadataExtension> IndexList> for TokenIndexes<'a, TMetadataExtension> where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, { - fn get_indexes(&'_ self) -> Box>> + '_> { - let v: Vec<&dyn Index>> = vec![&self.owner]; + fn get_indexes(&'_ self) -> Box>> + '_> { + let v: Vec<&dyn Index>> = vec![&self.owner]; Box::new(v.into_iter()) } } diff --git a/packages/cw721/src/testing/contract.rs b/packages/cw721/src/testing/contract.rs index 564662d12..81cb218ec 100644 --- a/packages/cw721/src/testing/contract.rs +++ b/packages/cw721/src/testing/contract.rs @@ -8,46 +8,35 @@ use crate::state::Cw721Config; pub struct Cw721Contract< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, - TMetadataResponse: CustomMsg, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub config: Cw721Config< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, >, } -impl< - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > Default +impl Default for Cw721Contract< 'static, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { @@ -57,59 +46,36 @@ where } } -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > - Cw721Execute< - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> + Cw721Execute for Cw721Contract< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } -impl< - 'a, - TMetadata, - TCustomResponseMessage, - TExtensionExecuteMsg, - TMetadataResponse, - TCollectionInfoExtension, - > Cw721Query +impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> + Cw721Query for Cw721Contract< 'a, - TMetadata, + TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, - TMetadataResponse, TCollectionInfoExtension, > where - TMetadata: Serialize + DeserializeOwned + Clone, + TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TExtensionExecuteMsg: CustomMsg, - TMetadataResponse: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } diff --git a/packages/cw721/src/testing/contract_tests.rs b/packages/cw721/src/testing/contract_tests.rs index 623be873b..1b2eb152e 100644 --- a/packages/cw721/src/testing/contract_tests.rs +++ b/packages/cw721/src/testing/contract_tests.rs @@ -29,7 +29,7 @@ const SYMBOL: &str = "MGK"; fn setup_contract( deps: DepsMut<'_>, -) -> Cw721Contract<'static, DefaultOptionMetadataExtension, Empty, Empty, Empty, Empty> { +) -> Cw721Contract<'static, DefaultOptionMetadataExtension, Empty, Empty, Empty> { let contract = Cw721Contract::default(); let msg = Cw721InstantiateMsg { name: CONTRACT_NAME.to_string(), @@ -57,8 +57,7 @@ fn setup_contract( #[test] fn proper_instantiation() { let mut deps = mock_dependencies(); - let contract = - Cw721Contract::::default(); + let contract = Cw721Contract::::default(); let msg = Cw721InstantiateMsg { name: CONTRACT_NAME.to_string(), @@ -128,7 +127,6 @@ fn proper_instantiation_with_collection_info() { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); diff --git a/packages/cw721/src/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index 790766736..63595855b 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -30,7 +30,6 @@ pub fn instantiate( DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.instantiate(deps, env, info, msg, "contract_name", "contract_version") @@ -50,7 +49,6 @@ pub fn execute( DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.execute(deps, env, info, msg) @@ -59,13 +57,12 @@ pub fn execute( pub fn query( deps: Deps, env: Env, - msg: Cw721QueryMsg, + msg: Cw721QueryMsg, ) -> StdResult { let contract = Cw721Contract::< DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.query(deps, env, msg) @@ -80,7 +77,6 @@ pub fn migrate( DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract.migrate(deps, env, msg, "contract_name", "contract_version") diff --git a/packages/cw721/src/testing/unit_tests.rs b/packages/cw721/src/testing/unit_tests.rs index a237fa6ca..2b6541cbb 100644 --- a/packages/cw721/src/testing/unit_tests.rs +++ b/packages/cw721/src/testing/unit_tests.rs @@ -26,7 +26,6 @@ fn proper_cw2_initialization() { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default() .instantiate( @@ -79,7 +78,6 @@ fn proper_owner_initialization() { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default() .instantiate( @@ -112,7 +110,6 @@ fn use_metadata_extension() { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); @@ -198,7 +195,6 @@ fn test_migrate() { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default(); contract @@ -234,7 +230,6 @@ fn test_migrate() { DefaultOptionMetadataExtension, Empty, Empty, - Empty, DefaultOptionCollectionInfoExtension, >::default() .migrate( From 9b8b140ed7b16ee7d0fc1b802c9d88c7237df55a Mon Sep 17 00:00:00 2001 From: mr-t Date: Fri, 8 Mar 2024 11:49:26 +0100 Subject: [PATCH 26/47] remove response structs to msg.rs --- contracts/cw2981-royalties/src/msg.rs | 5 +- contracts/cw721-base/src/execute.rs | 15 ++- contracts/cw721-base/src/query.rs | 9 +- contracts/cw721-base/src/state.rs | 7 +- .../cw721-expiration/src/contract_tests.rs | 7 +- contracts/cw721-expiration/src/execute.rs | 8 +- contracts/cw721-expiration/src/msg.rs | 20 +-- contracts/cw721-expiration/src/query.rs | 15 ++- contracts/cw721-expiration/src/state.rs | 7 +- .../cw721-non-transferable/examples/schema.rs | 7 +- packages/cw721/examples/schema.rs | 8 +- packages/cw721/src/execute.rs | 8 +- packages/cw721/src/helpers.rs | 10 +- packages/cw721/src/msg.rs | 78 ++++++++++-- packages/cw721/src/query.rs | 120 ++++++------------ packages/cw721/src/state.rs | 25 +++- packages/cw721/src/testing/contract.rs | 31 ++++- packages/cw721/src/testing/contract_tests.rs | 4 +- packages/cw721/src/testing/multi_tests.rs | 7 +- 19 files changed, 243 insertions(+), 148 deletions(-) diff --git a/contracts/cw2981-royalties/src/msg.rs b/contracts/cw2981-royalties/src/msg.rs index 468fb3fd0..b202158f0 100644 --- a/contracts/cw2981-royalties/src/msg.rs +++ b/contracts/cw2981-royalties/src/msg.rs @@ -3,9 +3,8 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, Empty, Uint128}; use cw721::{msg::Cw721QueryMsg, state::DefaultOptionCollectionInfoExtension}; use cw721_base::{ - msg::MinterResponse, - query::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, + msg::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, MinterResponse, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }, state::CollectionInfo, diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 06254c37d..68748bd57 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -6,8 +6,19 @@ use serde::Serialize; use crate::Cw721Contract; -impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> - Cw721Execute +impl< + 'a, + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > + Cw721Execute< + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > for Cw721Contract< 'a, TMetadataExtension, diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index 1b900e410..b6b4e0cd0 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -6,8 +6,13 @@ use serde::Serialize; use crate::Cw721Contract; -impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> - Cw721Query +impl< + 'a, + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > Cw721Query for Cw721Contract< 'a, TMetadataExtension, diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index be7fea632..4a3fb4f21 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -24,7 +24,12 @@ pub struct Cw721Contract< >, } -impl Default +impl< + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > Default for Cw721Contract< 'static, TMetadataExtension, diff --git a/contracts/cw721-expiration/src/contract_tests.rs b/contracts/cw721-expiration/src/contract_tests.rs index 0cde65959..bcdee3a98 100644 --- a/contracts/cw721-expiration/src/contract_tests.rs +++ b/contracts/cw721-expiration/src/contract_tests.rs @@ -7,10 +7,9 @@ use cosmwasm_std::{ }; use cw721::error::Cw721ContractError; -use cw721::msg::Cw721ExecuteMsg; -use cw721::query::{ - ApprovalResponse, NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, - TokensResponse, +use cw721::msg::{ + ApprovalResponse, Cw721ExecuteMsg, NftInfoResponse, OperatorResponse, OperatorsResponse, + OwnerOfResponse, TokensResponse, }; use cw721::receiver::Cw721ReceiveMsg; use cw721::state::{CollectionInfo, DefaultOptionCollectionInfoExtension, CREATOR, MINTER}; diff --git a/contracts/cw721-expiration/src/execute.rs b/contracts/cw721-expiration/src/execute.rs index ec3640d51..45f55aa86 100644 --- a/contracts/cw721-expiration/src/execute.rs +++ b/contracts/cw721-expiration/src/execute.rs @@ -12,7 +12,13 @@ use crate::{ CONTRACT_VERSION, }; -impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> +impl< + 'a, + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > Cw721ExpirationContract< 'a, TMetadataExtension, diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index 65683d509..77367577e 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -33,7 +33,7 @@ pub struct InstantiateMsg { pub enum QueryMsg { // -------- below adds `include_expired_nft` prop to cw721/src/msg.rs -------- /// Return the owner of the given token, error if token does not exist - #[returns(cw721::query::OwnerOfResponse)] + #[returns(cw721::msg::OwnerOfResponse)] OwnerOf { token_id: String, /// unset or false will filter out expired approvals, you must set to true to see them @@ -42,7 +42,7 @@ pub enum QueryMsg { include_expired_nft: Option, }, /// Return operator that can access all of the owner's tokens. - #[returns(cw721::query::ApprovalResponse)] + #[returns(cw721::msg::ApprovalResponse)] Approval { token_id: String, spender: String, @@ -51,7 +51,7 @@ pub enum QueryMsg { include_expired_nft: Option, }, /// Return approvals that a token has - #[returns(cw721::query::ApprovalsResponse)] + #[returns(cw721::msg::ApprovalsResponse)] Approvals { token_id: String, include_expired: Option, @@ -62,7 +62,7 @@ pub enum QueryMsg { /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract - #[returns(cw721::query::NftInfoResponse)] + #[returns(cw721::msg::NftInfoResponse)] NftInfo { token_id: String, /// unset or false will filter out expired nfts, you must set to true to see them @@ -72,7 +72,7 @@ pub enum QueryMsg { /// With MetaData Extension. /// Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization /// for clients - #[returns(cw721::query::AllNftInfoResponse)] + #[returns(cw721::msg::AllNftInfoResponse)] AllNftInfo { token_id: String, /// unset or false will filter out expired approvals, you must set to true to see them @@ -83,7 +83,7 @@ pub enum QueryMsg { /// With Enumerable extension. /// Returns all tokens owned by the given address, [] if unset. - #[returns(cw721::query::TokensResponse)] + #[returns(cw721::msg::TokensResponse)] Tokens { owner: String, start_after: Option, @@ -94,7 +94,7 @@ pub enum QueryMsg { /// With Enumerable extension. /// Requires pagination. Lists all token_ids controlled by the contract. - #[returns(cw721::query::TokensResponse)] + #[returns(cw721::msg::TokensResponse)] AllTokens { start_after: Option, limit: Option, @@ -104,14 +104,14 @@ pub enum QueryMsg { // -------- below is from cw721/src/msg.rs -------- /// Return approval of a given operator for all tokens of an owner, error if not set - #[returns(cw721::query::OperatorResponse)] + #[returns(cw721::msg::OperatorResponse)] Operator { owner: String, operator: String, include_expired: Option, }, /// List all operators that can access all of the owner's tokens - #[returns(cw721::query::OperatorsResponse)] + #[returns(cw721::msg::OperatorsResponse)] AllOperators { owner: String, /// unset or false will filter out expired items, you must set to true to see them @@ -120,7 +120,7 @@ pub enum QueryMsg { limit: Option, }, /// Total number of tokens issued, including all expired NFTs - #[returns(cw721::query::NumTokensResponse)] + #[returns(cw721::msg::NumTokensResponse)] NumTokens {}, #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index ef101173d..4221b4c03 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -1,14 +1,21 @@ use cosmwasm_std::{to_json_binary, Binary, CustomMsg, Deps, Env, StdResult}; -use cw721::query::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, Cw721Query, NftInfoResponse, - OwnerOfResponse, TokensResponse, +use cw721::msg::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, OwnerOfResponse, + TokensResponse, }; +use cw721::query::Cw721Query; use serde::de::DeserializeOwned; use serde::Serialize; use crate::{error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract}; -impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> +impl< + 'a, + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > Cw721ExpirationContract< 'a, TMetadataExtension, diff --git a/contracts/cw721-expiration/src/state.rs b/contracts/cw721-expiration/src/state.rs index 2c626f746..b8ba4bec6 100644 --- a/contracts/cw721-expiration/src/state.rs +++ b/contracts/cw721-expiration/src/state.rs @@ -26,7 +26,12 @@ pub struct Cw721ExpirationContract< >, } -impl Default +impl< + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > Default for Cw721ExpirationContract< 'static, TMetadataExtension, diff --git a/contracts/cw721-non-transferable/examples/schema.rs b/contracts/cw721-non-transferable/examples/schema.rs index 55616cda9..67b019b81 100644 --- a/contracts/cw721-non-transferable/examples/schema.rs +++ b/contracts/cw721-non-transferable/examples/schema.rs @@ -5,10 +5,9 @@ use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, s use cosmwasm_std::Empty; use cw721::{ - msg::{Cw721ExecuteMsg, MinterResponse}, - query::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, - NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + msg::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, Cw721ExecuteMsg, MinterResponse, + NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }, state::{CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, }; diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index d0d514ee8..6d0f00017 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -5,10 +5,10 @@ use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, s use cosmwasm_std::Empty; use cw721::{ - msg::{Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721QueryMsg}, - query::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, - NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + msg::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, Cw721ExecuteMsg, + Cw721InstantiateMsg, Cw721QueryMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, + OperatorsResponse, OwnerOfResponse, TokensResponse, }, receiver::Cw721ReceiveMsg, state::{CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index fc445aed1..c062b27c1 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -210,7 +210,9 @@ pub trait Cw721Execute< token_id: String, expires: Option, ) -> Result, Cw721ContractError> { - _update_approvals::(deps, &env, &info, &spender, &token_id, true, expires)?; + _update_approvals::( + deps, &env, &info, &spender, &token_id, true, expires, + )?; Ok(Response::new() .add_attribute("action", "approve") @@ -227,7 +229,9 @@ pub trait Cw721Execute< spender: String, token_id: String, ) -> Result, Cw721ContractError> { - _update_approvals::(deps, &env, &info, &spender, &token_id, false, None)?; + _update_approvals::( + deps, &env, &info, &spender, &token_id, false, None, + )?; Ok(Response::new() .add_attribute("action", "revoke") diff --git a/packages/cw721/src/helpers.rs b/packages/cw721/src/helpers.rs index f1d8f8ada..d6e5c7c79 100644 --- a/packages/cw721/src/helpers.rs +++ b/packages/cw721/src/helpers.rs @@ -1,10 +1,10 @@ use std::marker::PhantomData; -use crate::msg::{Cw721ExecuteMsg, Cw721QueryMsg}; -use crate::query::{ +use crate::msg::{ AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; +use crate::msg::{Cw721ExecuteMsg, Cw721QueryMsg}; use crate::state::CollectionInfo; use crate::Approval; use cosmwasm_schema::cw_serde; @@ -16,7 +16,11 @@ use serde::de::DeserializeOwned; use serde::Serialize; #[cw_serde] -pub struct Cw721Contract( +pub struct Cw721Contract< + TMetadataExtension, + TExtensionExecuteMsg: CustomMsg, + TCollectionInfoExtension, +>( pub Addr, pub PhantomData, pub PhantomData, diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index 32d4fa97f..9c64372f8 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -3,12 +3,8 @@ use cosmwasm_std::{Addr, Binary, Coin}; use cw_ownable::{Action, Ownership}; use cw_utils::Expiration; -use crate::query::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, NftInfoResponse, NumTokensResponse, - OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, -}; - use crate::state::CollectionInfo; +use crate::Approval; use cosmwasm_std::Empty; @@ -228,12 +224,6 @@ pub enum Cw721QueryMsg { GetCollectionInfoExtension { msg: TCollectionInfoExtension }, } -/// Shows who can mint these tokens -#[cw_serde] -pub struct MinterResponse { - pub minter: Option, -} - #[cw_serde] pub enum Cw721MigrateMsg { WithUpdate { @@ -248,3 +238,69 @@ pub struct CollectionInfoMsg { pub symbol: String, pub extension: TCollectionInfoExtension, } + +#[cw_serde] +pub struct OwnerOfResponse { + /// Owner of the token + pub owner: String, + /// If set this address is approved to transfer/send the token as well + pub approvals: Vec, +} + +#[cw_serde] +pub struct ApprovalResponse { + pub approval: Approval, +} + +#[cw_serde] +pub struct ApprovalsResponse { + pub approvals: Vec, +} + +#[cw_serde] +pub struct OperatorResponse { + pub approval: Approval, +} + +#[cw_serde] +pub struct OperatorsResponse { + pub operators: Vec, +} + +#[cw_serde] +pub struct NumTokensResponse { + pub count: u64, +} + +#[cw_serde] +pub struct NftInfoResponse { + /// Universal resource identifier for this NFT + /// Should point to a JSON file that conforms to the ERC721 + /// Metadata JSON Schema + pub token_uri: Option, + /// You can add any custom metadata here when you extend cw721-base + pub extension: TMetadataExtension, +} + +#[cw_serde] +pub struct AllNftInfoResponse { + /// Who can transfer the token + pub access: OwnerOfResponse, + /// Data on the token itself, + pub info: NftInfoResponse, +} + +#[cw_serde] +pub struct TokensResponse { + /// Contains all token_ids in lexicographical ordering + /// If there are more than `limit`, use `start_after` in future queries + /// to achieve pagination. + pub tokens: Vec, +} + +/// Deprecated: use Cw721QueryMsg::GetMinterOwnership instead! +/// Shows who can mint these tokens. +#[cw_serde] +pub struct MinterResponse { + pub minter: Option, +} diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index 0c9503bc6..c444d0e45 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -1,4 +1,3 @@ -use cosmwasm_schema::cw_serde; use cosmwasm_std::{ to_json_binary, Addr, Binary, BlockInfo, Deps, Empty, Env, Order, StdError, StdResult, Storage, }; @@ -9,7 +8,11 @@ use serde::de::DeserializeOwned; use serde::Serialize; use crate::{ - msg::{Cw721QueryMsg, MinterResponse}, + msg::{ + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, Cw721QueryMsg, MinterResponse, + NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, + TokensResponse, + }, state::{Approval, CollectionInfo, Cw721Config, NftInfo, CREATOR, MINTER}, }; @@ -162,8 +165,9 @@ where } fn query_num_tokens(&self, deps: Deps, _env: Env) -> StdResult { - let count = Cw721Config::::default() - .token_count(deps.storage)?; + let count = + Cw721Config::::default() + .token_count(deps.storage)?; Ok(NumTokensResponse { count }) } @@ -173,9 +177,10 @@ where _env: Env, token_id: String, ) -> StdResult> { - let info = Cw721Config::::default() - .nft_info - .load(deps.storage, &token_id)?; + let info = + Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; Ok(NftInfoResponse { token_uri: info.token_uri, extension: info.extension, @@ -189,9 +194,10 @@ where token_id: String, include_expired_approval: bool, ) -> StdResult { - let nft_info = Cw721Config::::default() - .nft_info - .load(deps.storage, &token_id)?; + let nft_info = + Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; Ok(OwnerOfResponse { owner: nft_info.owner.to_string(), approvals: humanize_approvals(&env.block, &nft_info, include_expired_approval), @@ -210,9 +216,10 @@ where let owner_addr = deps.api.addr_validate(&owner)?; let operator_addr = deps.api.addr_validate(&operator)?; - let info = Cw721Config::::default() - .operators - .may_load(deps.storage, (&owner_addr, &operator_addr))?; + let info = + Cw721Config::::default() + .operators + .may_load(deps.storage, (&owner_addr, &operator_addr))?; if let Some(expires) = info { if !include_expired_approval && expires.is_expired(&env.block) { @@ -269,9 +276,10 @@ where spender: String, include_expired_approval: bool, ) -> StdResult { - let token = Cw721Config::::default() - .nft_info - .load(deps.storage, &token_id)?; + let token = + Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; // token owner has absolute approval if token.owner == spender { @@ -310,9 +318,10 @@ where token_id: String, include_expired_approval: bool, ) -> StdResult { - let token = Cw721Config::::default() - .nft_info - .load(deps.storage, &token_id)?; + let token = + Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; let approvals: Vec<_> = token .approvals .into_iter() @@ -379,9 +388,10 @@ where token_id: String, include_expired_approval: bool, ) -> StdResult> { - let nft_info = Cw721Config::::default() - .nft_info - .load(deps.storage, &token_id)?; + let nft_info = + Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; Ok(AllNftInfoResponse { access: OwnerOfResponse { owner: nft_info.owner.to_string(), @@ -394,7 +404,12 @@ where }) } - fn query_extension(&self, _deps: Deps, _env: Env, _msg: TMetadataExtension) -> StdResult { + fn query_extension( + &self, + _deps: Deps, + _env: Env, + _msg: TMetadataExtension, + ) -> StdResult { Ok(Binary::default()) } @@ -440,62 +455,3 @@ pub fn humanize_approval(approval: &Approval) -> Approval { expires: approval.expires, } } - -#[cw_serde] -pub struct OwnerOfResponse { - /// Owner of the token - pub owner: String, - /// If set this address is approved to transfer/send the token as well - pub approvals: Vec, -} - -#[cw_serde] -pub struct ApprovalResponse { - pub approval: Approval, -} - -#[cw_serde] -pub struct ApprovalsResponse { - pub approvals: Vec, -} - -#[cw_serde] -pub struct OperatorResponse { - pub approval: Approval, -} - -#[cw_serde] -pub struct OperatorsResponse { - pub operators: Vec, -} - -#[cw_serde] -pub struct NumTokensResponse { - pub count: u64, -} - -#[cw_serde] -pub struct NftInfoResponse { - /// Universal resource identifier for this NFT - /// Should point to a JSON file that conforms to the ERC721 - /// Metadata JSON Schema - pub token_uri: Option, - /// You can add any custom metadata here when you extend cw721-base - pub extension: TMetadataExtension, -} - -#[cw_serde] -pub struct AllNftInfoResponse { - /// Who can transfer the token - pub access: OwnerOfResponse, - /// Data on the token itself, - pub info: NftInfoResponse, -} - -#[cw_serde] -pub struct TokensResponse { - /// Contains all token_ids in lexicographical ordering - /// If there are more than `limit`, use `start_after` in future queries - /// to achieve pagination. - pub tokens: Vec, -} diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index 84840d51b..3fa541426 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -37,14 +37,20 @@ pub struct Cw721Config< /// Stored as (granter, operator) giving operator full control over granter's account. /// NOTE: granter is the owner, so operator has only control for NFTs owned by granter! pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>, - pub nft_info: IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, TMetadataExtension>>, + pub nft_info: + IndexedMap<'a, &'a str, NftInfo, TokenIndexes<'a, TMetadataExtension>>, pub withdraw_address: Item<'a, String>, pub(crate) _custom_response: PhantomData, pub(crate) _custom_execute: PhantomData, } -impl Default +impl< + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > Default for Cw721Config< 'static, TMetadataExtension, @@ -69,7 +75,13 @@ where } } -impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> +impl< + 'a, + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > Cw721Config< 'a, TMetadataExtension, @@ -162,11 +174,14 @@ where pub owner: MultiIndex<'a, Addr, NftInfo, String>, } -impl<'a, TMetadataExtension> IndexList> for TokenIndexes<'a, TMetadataExtension> +impl<'a, TMetadataExtension> IndexList> + for TokenIndexes<'a, TMetadataExtension> where TMetadataExtension: Serialize + DeserializeOwned + Clone, { - fn get_indexes(&'_ self) -> Box>> + '_> { + fn get_indexes( + &'_ self, + ) -> Box>> + '_> { let v: Vec<&dyn Index>> = vec![&self.owner]; Box::new(v.into_iter()) } diff --git a/packages/cw721/src/testing/contract.rs b/packages/cw721/src/testing/contract.rs index 81cb218ec..bab97327c 100644 --- a/packages/cw721/src/testing/contract.rs +++ b/packages/cw721/src/testing/contract.rs @@ -26,7 +26,12 @@ pub struct Cw721Contract< >, } -impl Default +impl< + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > Default for Cw721Contract< 'static, TMetadataExtension, @@ -46,8 +51,19 @@ where } } -impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> - Cw721Execute +impl< + 'a, + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > + Cw721Execute< + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > for Cw721Contract< 'a, TMetadataExtension, @@ -63,8 +79,13 @@ where { } -impl<'a, TMetadataExtension, TCustomResponseMessage, TExtensionExecuteMsg, TCollectionInfoExtension> - Cw721Query +impl< + 'a, + TMetadataExtension, + TCustomResponseMessage, + TExtensionExecuteMsg, + TCollectionInfoExtension, + > Cw721Query for Cw721Contract< 'a, TMetadataExtension, diff --git a/packages/cw721/src/testing/contract_tests.rs b/packages/cw721/src/testing/contract_tests.rs index 1b2eb152e..603d64832 100644 --- a/packages/cw721/src/testing/contract_tests.rs +++ b/packages/cw721/src/testing/contract_tests.rs @@ -7,10 +7,10 @@ use cosmwasm_std::{ }; use crate::error::Cw721ContractError; -use crate::msg::{CollectionInfoMsg, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721QueryMsg}; -use crate::query::{ +use crate::msg::{ ApprovalResponse, NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, }; +use crate::msg::{CollectionInfoMsg, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721QueryMsg}; use crate::receiver::Cw721ReceiveMsg; use crate::state::{ CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension, CREATOR, diff --git a/packages/cw721/src/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index 63595855b..89f2211f9 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -1,8 +1,11 @@ use crate::{ error::Cw721ContractError, execute::Cw721Execute, - msg::{Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, Cw721QueryMsg, MinterResponse}, - query::{Cw721Query, OwnerOfResponse}, + msg::{ + Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, Cw721QueryMsg, MinterResponse, + OwnerOfResponse, + }, + query::Cw721Query, state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, }; use cosmwasm_std::{ From ed372159397bdfe009a2654e33f24c976f3c7d71 Mon Sep 17 00:00:00 2001 From: mr-t Date: Sat, 9 Mar 2024 16:24:40 +0100 Subject: [PATCH 27/47] cargo schema --- contracts/cw721-expiration/schema/cw721-expiration.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/cw721-expiration/schema/cw721-expiration.json b/contracts/cw721-expiration/schema/cw721-expiration.json index e5b5fa814..28d658416 100644 --- a/contracts/cw721-expiration/schema/cw721-expiration.json +++ b/contracts/cw721-expiration/schema/cw721-expiration.json @@ -1792,7 +1792,7 @@ "minter": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "MinterResponse", - "description": "Shows who can mint these tokens", + "description": "Deprecated: use Cw721QueryMsg::GetMinterOwnership instead! Shows who can mint these tokens.", "type": "object", "properties": { "minter": { From ffa74249dc267f652b12918477bde841487633f3 Mon Sep 17 00:00:00 2001 From: mr-t Date: Sat, 9 Mar 2024 17:39:02 +0100 Subject: [PATCH 28/47] move to dedicated fn --- packages/cw721/src/execute.rs | 20 +++++++++++++++++++- packages/cw721/src/query.rs | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index c062b27c1..ae518b25d 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -20,9 +20,13 @@ use crate::{ }; pub trait Cw721Execute< + // Metadata used for mint. TMetadataExtension, + // Defines for `CosmosMsg::Custom` in response. Barely used, so `Empty` can be used. TCustomResponseMessage, + // Message passed for updating metadata. TExtensionExecuteMsg, + // Message passed for updating collection info extension. TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, @@ -124,7 +128,9 @@ pub trait Cw721Execute< Cw721ExecuteMsg::UpdateCreatorOwnership(action) => { self.update_creator_ownership(deps, env, info, action) } - Cw721ExecuteMsg::Extension { msg: _ } => Ok(Response::default()), + Cw721ExecuteMsg::Extension { msg } => { + self.update_metadata_extension(deps, env, info, msg) + } Cw721ExecuteMsg::SetWithdrawAddress { address } => { self.set_withdraw_address(deps, &info.sender, address) } @@ -429,6 +435,18 @@ pub trait Cw721Execute< .add_attributes(ownership.into_attributes())) } + /// Allows creator to update onchain metadata. For now this is a no-op. + fn update_metadata_extension( + &self, + deps: DepsMut, + _env: Env, + info: MessageInfo, + _msg: TExtensionExecuteMsg, + ) -> Result, Cw721ContractError> { + CREATOR.assert_owner(deps.storage, &info.sender)?; + Ok(Response::new().add_attribute("action", "update_metadata_extension")) + } + fn set_withdraw_address( &self, deps: DepsMut, diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index c444d0e45..c6d5cc3eb 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -404,6 +404,7 @@ where }) } + /// No-op returning empty Binary fn query_extension( &self, _deps: Deps, @@ -413,6 +414,7 @@ where Ok(Binary::default()) } + /// No-op returning empty Binary fn query_collection_info_extension( &self, _deps: Deps, From 5db17ec0ad52c38f67ef83d4e45b286fe99ef149 Mon Sep 17 00:00:00 2001 From: mr-t Date: Sat, 9 Mar 2024 18:10:12 +0100 Subject: [PATCH 29/47] docs --- contracts/cw721-base/src/state.rs | 4 ++++ contracts/cw721-expiration/src/state.rs | 4 ++++ packages/cw721/src/execute.rs | 2 +- packages/cw721/src/query.rs | 8 ++++++-- packages/cw721/src/state.rs | 4 ++++ 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 4a3fb4f21..d7124263f 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -6,9 +6,13 @@ use serde::Serialize; pub struct Cw721Contract< 'a, + // Metadata defined in NftInfo (used for mint). TMetadataExtension, + // Defines for `CosmosMsg::Custom` in response. Barely used, so `Empty` can be used. TCustomResponseMessage, + // Message passed for updating metadata. TExtensionExecuteMsg, + // Extension defined in CollectionInfo. TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, diff --git a/contracts/cw721-expiration/src/state.rs b/contracts/cw721-expiration/src/state.rs index b8ba4bec6..dc1a06beb 100644 --- a/contracts/cw721-expiration/src/state.rs +++ b/contracts/cw721-expiration/src/state.rs @@ -6,9 +6,13 @@ use serde::Serialize; pub struct Cw721ExpirationContract< 'a, + // Metadata defined in NftInfo (used for mint). TMetadataExtension, + // Defines for `CosmosMsg::Custom` in response. Barely used, so `Empty` can be used. TCustomResponseMessage, + // Message passed for updating metadata. TExtensionExecuteMsg, + // Extension defined in CollectionInfo. TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index ae518b25d..27aae4fe7 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -20,7 +20,7 @@ use crate::{ }; pub trait Cw721Execute< - // Metadata used for mint. + // Metadata defined in NftInfo (used for mint). TMetadataExtension, // Defines for `CosmosMsg::Custom` in response. Barely used, so `Empty` can be used. TCustomResponseMessage, diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index c6d5cc3eb..204b4655d 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -19,8 +19,12 @@ use crate::{ pub const DEFAULT_LIMIT: u32 = 10; pub const MAX_LIMIT: u32 = 1000; -pub trait Cw721Query -where +pub trait Cw721Query< + // Metadata defined in NftInfo. + TMetadataExtension, + // Extension defined in CollectionInfo. + TCollectionInfoExtension, +> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index 3fa541426..b5572b9d4 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -22,9 +22,13 @@ pub type DefaultOptionMetadataExtension = Option; pub struct Cw721Config< 'a, + // Metadata defined in NftInfo (used for mint). TMetadataExtension, + // Defines for `CosmosMsg::Custom` in response. Barely used, so `Empty` can be used. TCustomResponseMessage, + // Message passed for updating metadata. TExtensionExecuteMsg, + // Extension defined in CollectionInfo. TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, From 1e32fac1b719fcde2e54bba9b1ce01bfe7f33de7 Mon Sep 17 00:00:00 2001 From: mr-t Date: Sat, 9 Mar 2024 18:17:09 +0100 Subject: [PATCH 30/47] rename --- contracts/cw2981-royalties/src/msg.rs | 2 - contracts/cw721-base/src/execute.rs | 8 ++-- contracts/cw721-base/src/query.rs | 6 +-- contracts/cw721-base/src/state.rs | 12 +++--- contracts/cw721-expiration/src/execute.rs | 12 +++--- contracts/cw721-expiration/src/query.rs | 8 ++-- contracts/cw721-expiration/src/state.rs | 12 +++--- packages/cw721/src/execute.rs | 48 ++++++++++++----------- packages/cw721/src/helpers.rs | 10 ++--- packages/cw721/src/msg.rs | 4 +- packages/cw721/src/state.rs | 18 ++++----- packages/cw721/src/testing/contract.rs | 26 ++++++------ 12 files changed, 84 insertions(+), 82 deletions(-) diff --git a/contracts/cw2981-royalties/src/msg.rs b/contracts/cw2981-royalties/src/msg.rs index b202158f0..7e4921c4f 100644 --- a/contracts/cw2981-royalties/src/msg.rs +++ b/contracts/cw2981-royalties/src/msg.rs @@ -137,11 +137,9 @@ pub enum QueryMsg { // -- below queries, Extension and GetCollectionInfoExtension, are just dummies, since type annotations are required for // -- TMetadataExtension and TCollectionInfoExtension, Error: // -- "type annotations needed: cannot infer type for type parameter `TMetadataExtension` declared on the enum `Cw721QueryMsg`" - /// Do not use - dummy extension query, needed for inferring type parameter during compile #[returns(())] Extension { msg: Extension }, - /// Do not use - dummy collection info extension query, needed for inferring type parameter during compile #[returns(())] GetCollectionInfoExtension { msg: DefaultOptionCollectionInfoExtension, diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 68748bd57..7054005a9 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -10,26 +10,26 @@ impl< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Cw721Execute< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > for Cw721Contract< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index b6b4e0cd0..9e371dde4 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -10,20 +10,20 @@ impl< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Cw721Query for Cw721Contract< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index d7124263f..8db42033c 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -11,19 +11,19 @@ pub struct Cw721Contract< // Defines for `CosmosMsg::Custom` in response. Barely used, so `Empty` can be used. TCustomResponseMessage, // Message passed for updating metadata. - TExtensionExecuteMsg, + TMetadataExtensionMsg, // Extension defined in CollectionInfo. TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub config: Cw721Config< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, >, } @@ -31,19 +31,19 @@ pub struct Cw721Contract< impl< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Default for Cw721Contract< 'static, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { diff --git a/contracts/cw721-expiration/src/execute.rs b/contracts/cw721-expiration/src/execute.rs index 45f55aa86..56ab2c524 100644 --- a/contracts/cw721-expiration/src/execute.rs +++ b/contracts/cw721-expiration/src/execute.rs @@ -16,20 +16,20 @@ impl< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Cw721ExpirationContract< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { // -- instantiate -- @@ -46,7 +46,7 @@ where let contract = Cw721ExpirationContract::< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, >::default(); contract @@ -75,12 +75,12 @@ where deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721ExecuteMsg, + msg: Cw721ExecuteMsg, ) -> Result, ContractError> { let contract = Cw721ExpirationContract::< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, >::default(); match msg { diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index 4221b4c03..f921eccf2 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -13,20 +13,20 @@ impl< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Cw721ExpirationContract< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub fn query( @@ -38,7 +38,7 @@ where let contract = Cw721ExpirationContract::< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, >::default(); match msg { diff --git a/contracts/cw721-expiration/src/state.rs b/contracts/cw721-expiration/src/state.rs index dc1a06beb..55b5f8a86 100644 --- a/contracts/cw721-expiration/src/state.rs +++ b/contracts/cw721-expiration/src/state.rs @@ -11,12 +11,12 @@ pub struct Cw721ExpirationContract< // Defines for `CosmosMsg::Custom` in response. Barely used, so `Empty` can be used. TCustomResponseMessage, // Message passed for updating metadata. - TExtensionExecuteMsg, + TMetadataExtensionMsg, // Extension defined in CollectionInfo. TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub expiration_days: Item<'a, u16>, // max 65535 days @@ -25,7 +25,7 @@ pub struct Cw721ExpirationContract< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, >, } @@ -33,19 +33,19 @@ pub struct Cw721ExpirationContract< impl< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Default for Cw721ExpirationContract< 'static, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index 27aae4fe7..a21676ab2 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -25,26 +25,26 @@ pub trait Cw721Execute< // Defines for `CosmosMsg::Custom` in response. Barely used, so `Empty` can be used. TCustomResponseMessage, // Message passed for updating metadata. - TExtensionExecuteMsg, + TMetadataExtensionMsg, // Message passed for updating collection info extension. - TCollectionInfoExtension, + TCollectionInfoExtensionMsg, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, + TMetadataExtensionMsg: CustomMsg, + TCollectionInfoExtensionMsg: Serialize + DeserializeOwned + Clone, { fn instantiate( &self, deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721InstantiateMsg, + msg: Cw721InstantiateMsg, contract_name: &str, contract_version: &str, ) -> Result, Cw721ContractError> { cw2::set_contract_version(deps.storage, contract_name, contract_version)?; - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let collection_info = CollectionInfo { name: msg.name, symbol: msg.symbol, @@ -84,7 +84,11 @@ pub trait Cw721Execute< deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721ExecuteMsg, + msg: Cw721ExecuteMsg< + TMetadataExtension, + TMetadataExtensionMsg, + TCollectionInfoExtensionMsg, + >, ) -> Result, Cw721ContractError> { match msg { Cw721ExecuteMsg::UpdateCollectionInfo { collection_info } => { @@ -265,8 +269,8 @@ pub trait Cw721Execute< let config = Cw721Config::< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, - TCollectionInfoExtension, + TMetadataExtensionMsg, + TCollectionInfoExtensionMsg, >::default(); config .operators @@ -291,8 +295,8 @@ pub trait Cw721Execute< let config = Cw721Config::< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, - TCollectionInfoExtension, + TMetadataExtensionMsg, + TCollectionInfoExtensionMsg, >::default(); config .operators @@ -314,8 +318,8 @@ pub trait Cw721Execute< let config = Cw721Config::< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, - TCollectionInfoExtension, + TMetadataExtensionMsg, + TCollectionInfoExtensionMsg, >::default(); let token = config.nft_info.load(deps.storage, &token_id)?; check_can_send(deps.as_ref(), &env, &info, &token)?; @@ -353,7 +357,7 @@ pub trait Cw721Execute< deps: DepsMut, info: MessageInfo, env: Env, - msg: CollectionInfoMsg, + msg: CollectionInfoMsg, ) -> Result, Cw721ContractError> { CREATOR.assert_owner(deps.storage, &info.sender)?; let collection_info = CollectionInfo { @@ -363,7 +367,7 @@ pub trait Cw721Execute< updated_at: env.block.time, }; let config = - Cw721Config::::default( + Cw721Config::::default( ); config .collection_info @@ -441,7 +445,7 @@ pub trait Cw721Execute< deps: DepsMut, _env: Env, info: MessageInfo, - _msg: TExtensionExecuteMsg, + _msg: TMetadataExtensionMsg, ) -> Result, Cw721ContractError> { CREATOR.assert_owner(deps.storage, &info.sender)?; Ok(Response::new().add_attribute("action", "update_metadata_extension")) @@ -458,8 +462,8 @@ pub trait Cw721Execute< let config = Cw721Config::< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, - TCollectionInfoExtension, + TMetadataExtensionMsg, + TCollectionInfoExtensionMsg, >::default(); config.withdraw_address.save(deps.storage, &address)?; Ok(Response::new() @@ -476,8 +480,8 @@ pub trait Cw721Execute< let config = Cw721Config::< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, - TCollectionInfoExtension, + TMetadataExtensionMsg, + TCollectionInfoExtensionMsg, >::default(); let address = config.withdraw_address.may_load(storage)?; match address { @@ -499,8 +503,8 @@ pub trait Cw721Execute< let withdraw_address = Cw721Config::< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, - TCollectionInfoExtension, + TMetadataExtensionMsg, + TCollectionInfoExtensionMsg, >::default() .withdraw_address .may_load(storage)?; diff --git a/packages/cw721/src/helpers.rs b/packages/cw721/src/helpers.rs index d6e5c7c79..259016ab0 100644 --- a/packages/cw721/src/helpers.rs +++ b/packages/cw721/src/helpers.rs @@ -18,18 +18,18 @@ use serde::Serialize; #[cw_serde] pub struct Cw721Contract< TMetadataExtension, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension, >( pub Addr, pub PhantomData, - pub PhantomData, + pub PhantomData, pub PhantomData, ); #[allow(dead_code)] -impl - Cw721Contract +impl + Cw721Contract where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, @@ -40,7 +40,7 @@ where pub fn call( &self, - msg: Cw721ExecuteMsg, + msg: Cw721ExecuteMsg, ) -> StdResult { let msg = to_json_binary(&msg)?; Ok(WasmMsg::Execute { diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index 9c64372f8..809966f65 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -9,7 +9,7 @@ use crate::Approval; use cosmwasm_std::Empty; #[cw_serde] -pub enum Cw721ExecuteMsg { +pub enum Cw721ExecuteMsg { #[deprecated(since = "0.19.0", note = "Please use UpdateMinterOwnership instead")] UpdateOwnership(Action), UpdateMinterOwnership(Action), @@ -75,7 +75,7 @@ pub enum Cw721ExecuteMsg` in response. Barely used, so `Empty` can be used. TCustomResponseMessage, // Message passed for updating metadata. - TExtensionExecuteMsg, + TMetadataExtensionMsg, // Extension defined in CollectionInfo. TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { /// Note: replaces deprecated/legacy key "nft_info"! @@ -46,25 +46,25 @@ pub struct Cw721Config< pub withdraw_address: Item<'a, String>, pub(crate) _custom_response: PhantomData, - pub(crate) _custom_execute: PhantomData, + pub(crate) _custom_execute: PhantomData, } impl< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Default for Cw721Config< 'static, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { @@ -83,19 +83,19 @@ impl< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Cw721Config< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn new( diff --git a/packages/cw721/src/testing/contract.rs b/packages/cw721/src/testing/contract.rs index bab97327c..4e8c571e5 100644 --- a/packages/cw721/src/testing/contract.rs +++ b/packages/cw721/src/testing/contract.rs @@ -10,18 +10,18 @@ pub struct Cw721Contract< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub config: Cw721Config< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, >, } @@ -29,19 +29,19 @@ pub struct Cw721Contract< impl< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Default for Cw721Contract< 'static, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { @@ -55,26 +55,26 @@ impl< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Cw721Execute< TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > for Cw721Contract< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } @@ -83,20 +83,20 @@ impl< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > Cw721Query for Cw721Contract< 'a, TMetadataExtension, TCustomResponseMessage, - TExtensionExecuteMsg, + TMetadataExtensionMsg, TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, - TExtensionExecuteMsg: CustomMsg, + TMetadataExtensionMsg: CustomMsg, TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } From 53754d7dd52a30118c666bd466a4c99af768a356 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 25 Apr 2024 19:27:57 +0200 Subject: [PATCH 31/47] cargo clippy --- packages/cw721/src/testing/unit_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cw721/src/testing/unit_tests.rs b/packages/cw721/src/testing/unit_tests.rs index 2b6541cbb..31783ef78 100644 --- a/packages/cw721/src/testing/unit_tests.rs +++ b/packages/cw721/src/testing/unit_tests.rs @@ -275,7 +275,7 @@ fn test_migrate() { name: "legacy_name".to_string(), symbol: "legacy_symbol".to_string(), extension: None, - updated_at: env.clone().block.time, + updated_at: env.block.time, }; assert_eq!(collection_info, legacy_contract_info); From dc8a01fd917128c4bca637ba5608a1158094e55e Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 25 Apr 2024 19:29:12 +0200 Subject: [PATCH 32/47] add build script --- build.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 build.sh diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..b7b90ac70 --- /dev/null +++ b/build.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +## Compiles and optimizes contracts + +set -o errexit -o nounset -o pipefail +command -v shellcheck >/dev/null && shellcheck "$0" + +# build Ark proxies +cd "$(git rev-parse --show-toplevel)" +docker run --rm -v "$(pwd)":/code --platform linux/amd64 \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/workspace-optimizer:0.15.1 # https://hub.docker.com/r/cosmwasm/workspace-optimizer/tags +ls -al ./artifacts/*wasm \ No newline at end of file From 9e06b0b2634bf1c36d7047599de1e24fbe59000d Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 30 Apr 2024 13:26:51 +0200 Subject: [PATCH 33/47] undo collection info extension and royalty info additions, will be added in next pr --- contracts/cw2981-royalties/examples/schema.rs | 3 +- .../schema/cw2981-royalties.json | 765 +----------------- contracts/cw2981-royalties/src/lib.rs | 17 +- contracts/cw2981-royalties/src/msg.rs | 32 +- contracts/cw721-base/src/execute.rs | 24 +- contracts/cw721-base/src/lib.rs | 40 +- contracts/cw721-base/src/query.rs | 18 +- contracts/cw721-base/src/state.rs | 27 +- contracts/cw721-expiration/examples/schema.rs | 5 +- .../schema/cw721-expiration.json | 555 +------------ .../cw721-expiration/src/contract_tests.rs | 58 +- contracts/cw721-expiration/src/execute.rs | 25 +- contracts/cw721-expiration/src/lib.rs | 57 +- contracts/cw721-expiration/src/msg.rs | 24 +- contracts/cw721-expiration/src/query.rs | 33 +- contracts/cw721-expiration/src/state.rs | 21 +- .../cw721-fixed-price/examples/schema.rs | 3 +- .../schema/cw721-fixed-price.json | 90 --- contracts/cw721-fixed-price/src/contract.rs | 25 +- contracts/cw721-fixed-price/src/msg.rs | 3 +- .../cw721-non-transferable/examples/schema.rs | 17 +- .../schema/collection_info.json | 104 +-- .../schema/execute_msg.json | 145 ---- .../schema/instantiate_msg.json | 104 +-- .../schema/minter_response.json | 2 +- .../schema/query_msg.json | 41 - contracts/cw721-non-transferable/src/lib.rs | 21 +- contracts/cw721-non-transferable/src/msg.rs | 29 +- .../cw721-receiver-tester/tests/multitest.rs | 13 +- packages/cw721/examples/schema.rs | 24 +- packages/cw721/schema/collection_info.json | 104 +-- packages/cw721/schema/execute_msg.json | 145 ---- packages/cw721/schema/instantiate_msg.json | 105 +-- packages/cw721/schema/query_msg.json | 153 ---- packages/cw721/src/execute.rs | 164 +--- packages/cw721/src/helpers.rs | 31 +- packages/cw721/src/lib.rs | 2 +- packages/cw721/src/msg.rs | 43 +- packages/cw721/src/query.rs | 136 ++-- packages/cw721/src/state.rs | 68 +- packages/cw721/src/testing/contract.rs | 76 +- packages/cw721/src/testing/contract_tests.rs | 208 +---- packages/cw721/src/testing/multi_tests.rs | 234 ++---- packages/cw721/src/testing/unit_tests.rs | 144 ++-- 44 files changed, 358 insertions(+), 3580 deletions(-) diff --git a/contracts/cw2981-royalties/examples/schema.rs b/contracts/cw2981-royalties/examples/schema.rs index 5aebaa36e..055a210ad 100644 --- a/contracts/cw2981-royalties/examples/schema.rs +++ b/contracts/cw2981-royalties/examples/schema.rs @@ -1,11 +1,10 @@ use cosmwasm_schema::write_api; use cw2981_royalties::{msg::QueryMsg, ExecuteMsg, InstantiateMsg}; -use cw721::state::DefaultOptionCollectionInfoExtension; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: InstantiateMsg, execute: ExecuteMsg, query: QueryMsg, } diff --git a/contracts/cw2981-royalties/schema/cw2981-royalties.json b/contracts/cw2981-royalties/schema/cw2981-royalties.json index 59f356efe..3040363db 100644 --- a/contracts/cw2981-royalties/schema/cw2981-royalties.json +++ b/contracts/cw2981-royalties/schema/cw2981-royalties.json @@ -11,23 +11,6 @@ "symbol" ], "properties": { - "collection_info_extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "creator": { - "description": "The creator is the only who can update collection info.", - "type": [ - "string", - "null" - ] - }, "minter": { "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", "type": [ @@ -50,100 +33,13 @@ ] } }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "additionalProperties": false }, "execute": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ExecuteMsg", "oneOf": [ { - "deprecated": true, "type": "object", "required": [ "update_ownership" @@ -155,52 +51,6 @@ }, "additionalProperties": false }, - { - "type": "object", - "required": [ - "update_minter_ownership" - ], - "properties": { - "update_minter_ownership": { - "$ref": "#/definitions/Action" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "update_creator_ownership" - ], - "properties": { - "update_creator_ownership": { - "$ref": "#/definitions/Action" - } - }, - "additionalProperties": false - }, - { - "description": "Update the collection, can only called by the contract creator", - "type": "object", - "required": [ - "update_collection_info" - ], - "properties": { - "update_collection_info": { - "type": "object", - "required": [ - "collection_info" - ], - "properties": { - "collection_info": { - "$ref": "#/definitions/CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "description": "Transfer is a base message to move a token to another account without triggering actions", "type": "object", @@ -574,10 +424,6 @@ } ] }, - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, "Binary": { "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" @@ -597,84 +443,6 @@ } } }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "name", - "symbol" - ], - "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, "Empty": { "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", "type": "object" @@ -805,22 +573,6 @@ }, "additionalProperties": false }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, "Timestamp": { "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", "allOf": [ @@ -1084,7 +836,6 @@ "additionalProperties": false }, { - "deprecated": true, "type": "object", "required": [ "contract_info" @@ -1112,7 +863,6 @@ "additionalProperties": false }, { - "deprecated": true, "type": "object", "required": [ "ownership" @@ -1125,32 +875,6 @@ }, "additionalProperties": false }, - { - "type": "object", - "required": [ - "get_minter_ownership" - ], - "properties": { - "get_minter_ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_creator_ownership" - ], - "properties": { - "get_creator_ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", "type": "object", @@ -1270,7 +994,6 @@ }, { "description": "Return the minter", - "deprecated": true, "type": "object", "required": [ "minter" @@ -1297,7 +1020,6 @@ "additionalProperties": false }, { - "description": "Do not use - dummy extension query, needed for inferring type parameter during compile", "type": "object", "required": [ "extension" @@ -1321,91 +1043,9 @@ } }, "additionalProperties": false - }, - { - "description": "Do not use - dummy collection info extension query, needed for inferring type parameter during compile", - "type": "object", - "required": [ - "get_collection_info_extension" - ], - "properties": { - "get_collection_info_extension": { - "type": "object", - "properties": { - "msg": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false } ], "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, "Metadata": { "type": "object", "properties": { @@ -1485,42 +1125,18 @@ }, "additionalProperties": false }, - "RoyaltyInfo": { + "Trait": { "type": "object", "required": [ - "payment_address", - "share" + "trait_type", + "value" ], "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Trait": { - "type": "object", - "required": [ - "trait_type", - "value" - ], - "properties": { - "display_type": { - "type": [ - "string", - "null" - ] + "display_type": { + "type": [ + "string", + "null" + ] }, "trait_type": { "type": "string" @@ -1534,10 +1150,6 @@ "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", "type": "string" - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" } } }, @@ -2165,47 +1777,21 @@ }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Empty", + "title": "CollectionInfo", "type": "object", "required": [ - "extension", "name", - "symbol", - "updated_at" + "symbol" ], "properties": { - "extension": { - "$ref": "#/definitions/Empty" - }, "name": { "type": "string" }, "symbol": { "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false, - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "additionalProperties": false }, "extension": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -2214,340 +1800,21 @@ }, "get_collection_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo", + "title": "CollectionInfo", "type": "object", "required": [ "name", - "symbol", - "updated_at" + "symbol" ], "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, "name": { "type": "string" }, "symbol": { "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "get_collection_info_extension": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Null", - "type": "null" - }, - "get_creator_ownership": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Ownership_for_Addr", - "description": "The contract's ownership info", - "type": "object", - "properties": { - "owner": { - "description": "The contract's current owner. `None` if the ownership has been renounced.", - "anyOf": [ - { - "$ref": "#/definitions/Addr" - }, - { - "type": "null" - } - ] - }, - "pending_expiry": { - "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "pending_owner": { - "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", - "anyOf": [ - { - "$ref": "#/definitions/Addr" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "get_minter_ownership": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Ownership_for_Addr", - "description": "The contract's ownership info", - "type": "object", - "properties": { - "owner": { - "description": "The contract's current owner. `None` if the ownership has been renounced.", - "anyOf": [ - { - "$ref": "#/definitions/Addr" - }, - { - "type": "null" - } - ] - }, - "pending_expiry": { - "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "pending_owner": { - "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", - "anyOf": [ - { - "$ref": "#/definitions/Addr" - }, - { - "type": "null" - } - ] } }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "additionalProperties": false }, "get_withdraw_address": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -2560,7 +1827,7 @@ "minter": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "MinterResponse", - "description": "Shows who can mint these tokens", + "description": "Deprecated: use Cw721QueryMsg::GetMinterOwnership instead! Shows who can mint these tokens.", "type": "object", "properties": { "minter": { diff --git a/contracts/cw2981-royalties/src/lib.rs b/contracts/cw2981-royalties/src/lib.rs index 30461eecf..3a138f7e4 100644 --- a/contracts/cw2981-royalties/src/lib.rs +++ b/contracts/cw2981-royalties/src/lib.rs @@ -2,7 +2,6 @@ pub mod error; pub mod msg; pub mod query; -use cw721::state::DefaultOptionCollectionInfoExtension; pub use query::{check_royalties, query_royalties_info}; use cosmwasm_schema::cw_serde; @@ -50,10 +49,8 @@ pub type Extension = Option; pub type MintExtension = Option; -pub type Cw2981Contract<'a> = - Cw721Contract<'a, Extension, Empty, Empty, DefaultOptionCollectionInfoExtension>; -pub type ExecuteMsg = - cw721_base::msg::ExecuteMsg; +pub type Cw2981Contract<'a> = Cw721Contract<'a, Extension, Empty, Empty>; +pub type ExecuteMsg = cw721_base::msg::ExecuteMsg; #[cfg(not(feature = "library"))] pub mod entry { @@ -69,7 +66,7 @@ pub mod entry { mut deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { Ok(Cw2981Contract::default().instantiate( deps.branch(), @@ -142,9 +139,7 @@ mod tests { let init_msg = InstantiateMsg { name: "SpaceShips".to_string(), symbol: "SPACE".to_string(), - collection_info_extension: None, minter: None, - creator: None, withdraw_address: None, }; entry::instantiate(deps.as_mut(), mock_env(), info.clone(), init_msg).unwrap(); @@ -181,9 +176,7 @@ mod tests { let init_msg = InstantiateMsg { name: "SpaceShips".to_string(), symbol: "SPACE".to_string(), - collection_info_extension: None, minter: None, - creator: None, withdraw_address: None, }; entry::instantiate(deps.as_mut(), mock_env(), info.clone(), init_msg).unwrap(); @@ -214,9 +207,7 @@ mod tests { let init_msg = InstantiateMsg { name: "SpaceShips".to_string(), symbol: "SPACE".to_string(), - collection_info_extension: None, minter: None, - creator: None, withdraw_address: None, }; entry::instantiate(deps.as_mut(), mock_env(), info.clone(), init_msg).unwrap(); @@ -255,9 +246,7 @@ mod tests { let init_msg = InstantiateMsg { name: "SpaceShips".to_string(), symbol: "SPACE".to_string(), - collection_info_extension: None, minter: None, - creator: None, withdraw_address: None, }; let env = mock_env(); diff --git a/contracts/cw2981-royalties/src/msg.rs b/contracts/cw2981-royalties/src/msg.rs index 7e4921c4f..9fa6bf1a1 100644 --- a/contracts/cw2981-royalties/src/msg.rs +++ b/contracts/cw2981-royalties/src/msg.rs @@ -1,7 +1,7 @@ use crate::Extension; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Empty, Uint128}; -use cw721::{msg::Cw721QueryMsg, state::DefaultOptionCollectionInfoExtension}; +use cosmwasm_std::{Addr, Uint128}; +use cw721::msg::Cw721QueryMsg; use cw721_base::{ msg::{ AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, MinterResponse, NftInfoResponse, @@ -76,25 +76,17 @@ pub enum QueryMsg { #[returns(NumTokensResponse)] NumTokens {}, - #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] - #[returns(CollectionInfo)] + #[returns(CollectionInfo)] ContractInfo {}, /// With MetaData Extension. /// Returns top-level metadata about the contract - #[returns(CollectionInfo)] + #[returns(CollectionInfo)] GetCollectionInfo {}, - #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] #[returns(Ownership)] Ownership {}, - #[returns(Ownership)] - GetMinterOwnership {}, - - #[returns(Ownership)] - GetCreatorOwnership {}, - /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract @@ -127,7 +119,6 @@ pub enum QueryMsg { }, /// Return the minter - #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] #[returns(MinterResponse)] Minter {}, @@ -139,11 +130,6 @@ pub enum QueryMsg { // -- "type annotations needed: cannot infer type for type parameter `TMetadataExtension` declared on the enum `Cw721QueryMsg`" #[returns(())] Extension { msg: Extension }, - - #[returns(())] - GetCollectionInfoExtension { - msg: DefaultOptionCollectionInfoExtension, - }, } // impl Default for Cw2981QueryMsg { @@ -154,8 +140,8 @@ pub enum QueryMsg { // impl CustomMsg for Cw2981QueryMsg {} -impl From for Cw721QueryMsg { - fn from(msg: QueryMsg) -> Cw721QueryMsg { +impl From for Cw721QueryMsg { + fn from(msg: QueryMsg) -> Cw721QueryMsg { match msg { QueryMsg::OwnerOf { token_id, @@ -165,9 +151,7 @@ impl From for Cw721QueryMsg Cw721QueryMsg::NumTokens {}, - #[allow(deprecated)] - QueryMsg::ContractInfo {} => Cw721QueryMsg::GetCollectionInfo {}, - QueryMsg::GetCollectionInfo {} => Cw721QueryMsg::GetCollectionInfo {}, + QueryMsg::ContractInfo {} => Cw721QueryMsg::ContractInfo {}, QueryMsg::NftInfo { token_id } => Cw721QueryMsg::NftInfo { token_id }, QueryMsg::AllNftInfo { token_id, @@ -190,8 +174,6 @@ impl From for Cw721QueryMsg Cw721QueryMsg::Minter {}, - QueryMsg::GetMinterOwnership {} => Cw721QueryMsg::GetMinterOwnership {}, - QueryMsg::GetCreatorOwnership {} => Cw721QueryMsg::GetCreatorOwnership {}, QueryMsg::GetWithdrawAddress {} => Cw721QueryMsg::GetWithdrawAddress {}, QueryMsg::AllOperators { owner, diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 7054005a9..83a4e8295 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -6,30 +6,12 @@ use serde::Serialize; use crate::Cw721Contract; -impl< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > - Cw721Execute< - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > - for Cw721Contract< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> + Cw721Execute + for Cw721Contract<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index 450327fcd..80c1d23fb 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -32,7 +32,7 @@ pub mod entry { execute::Cw721Execute, msg::{Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, Cw721QueryMsg}, query::Cw721Query, - state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, + state::DefaultOptionMetadataExtension, }; // This makes a conscious choice on the various generics used by the contract @@ -41,14 +41,9 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721InstantiateMsg, + msg: Cw721InstantiateMsg, ) -> Result { - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Contract::::default(); contract.instantiate(deps, env, info, msg, CONTRACT_NAME, CONTRACT_VERSION) } @@ -57,18 +52,9 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721ExecuteMsg< - DefaultOptionMetadataExtension, - Empty, - DefaultOptionCollectionInfoExtension, - >, + msg: Cw721ExecuteMsg, ) -> Result { - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Contract::::default(); contract.execute(deps, env, info, msg) } @@ -76,14 +62,9 @@ pub mod entry { pub fn query( deps: Deps, env: Env, - msg: Cw721QueryMsg, + msg: Cw721QueryMsg, ) -> StdResult { - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Contract::::default(); contract.query(deps, env, msg) } @@ -93,12 +74,7 @@ pub mod entry { env: Env, msg: Cw721MigrateMsg, ) -> Result { - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Contract::::default(); contract.migrate(deps, env, msg, CONTRACT_NAME, CONTRACT_VERSION) } } diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index 9e371dde4..3b454892c 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -6,24 +6,12 @@ use serde::Serialize; use crate::Cw721Contract; -impl< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > Cw721Query - for Cw721Contract< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> + Cw721Query + for Cw721Contract<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index 8db42033c..d90fa91d5 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -12,39 +12,18 @@ pub struct Cw721Contract< TCustomResponseMessage, // Message passed for updating metadata. TMetadataExtensionMsg, - // Extension defined in CollectionInfo. - TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { - pub config: Cw721Config< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - >, + pub config: Cw721Config<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg>, } -impl< - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > Default - for Cw721Contract< - 'static, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > +impl Default + for Cw721Contract<'static, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { Self { diff --git a/contracts/cw721-expiration/examples/schema.rs b/contracts/cw721-expiration/examples/schema.rs index 08836096e..2a2acfe44 100644 --- a/contracts/cw721-expiration/examples/schema.rs +++ b/contracts/cw721-expiration/examples/schema.rs @@ -1,11 +1,10 @@ use cosmwasm_schema::write_api; use cosmwasm_std::Empty; -use cw721::state::DefaultOptionCollectionInfoExtension; use cw721_expiration::msg::{InstantiateMsg, QueryMsg}; fn main() { write_api! { - instantiate: InstantiateMsg, - query: QueryMsg, + instantiate: InstantiateMsg, + query: QueryMsg, } } diff --git a/contracts/cw721-expiration/schema/cw721-expiration.json b/contracts/cw721-expiration/schema/cw721-expiration.json index 28d658416..bada44971 100644 --- a/contracts/cw721-expiration/schema/cw721-expiration.json +++ b/contracts/cw721-expiration/schema/cw721-expiration.json @@ -12,23 +12,6 @@ "symbol" ], "properties": { - "collection_info_extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "creator": { - "description": "The creator is the only who can update collection info.", - "type": [ - "string", - "null" - ] - }, "expiration_days": { "description": "max 65535 days", "type": "integer", @@ -57,93 +40,7 @@ ] } }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "additionalProperties": false }, "execute": null, "query": { @@ -495,7 +392,6 @@ "additionalProperties": false }, { - "deprecated": true, "type": "object", "required": [ "contract_info" @@ -523,7 +419,6 @@ "additionalProperties": false }, { - "deprecated": true, "type": "object", "required": [ "ownership" @@ -549,22 +444,8 @@ }, "additionalProperties": false }, - { - "type": "object", - "required": [ - "get_creator_ownership" - ], - "properties": { - "get_creator_ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "description": "Return the minter", - "deprecated": true, "type": "object", "required": [ "minter" @@ -599,32 +480,6 @@ }, "additionalProperties": false }, - { - "description": "This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg`", - "type": "object", - "required": [ - "get_collection_info_extension" - ], - "properties": { - "get_collection_info_extension": { - "type": "object", - "properties": { - "msg": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "type": "object", "required": [ @@ -640,93 +495,9 @@ } ], "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, "Empty": { "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", "type": "object" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" } } }, @@ -1323,121 +1094,21 @@ }, "contract_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo", + "title": "CollectionInfo", "type": "object", "required": [ "name", - "symbol", - "updated_at" + "symbol" ], "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, "name": { "type": "string" }, "symbol": { "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "additionalProperties": false }, "extension": { "$schema": "http://json-schema.org/draft-07/schema#", @@ -1446,233 +1117,21 @@ }, "get_collection_info": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo", + "title": "CollectionInfo", "type": "object", "required": [ "name", - "symbol", - "updated_at" + "symbol" ], "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, "name": { "type": "string" }, "symbol": { "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "get_collection_info_extension": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Null", - "type": "null" - }, - "get_creator_ownership": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Ownership_for_Addr", - "description": "The contract's ownership info", - "type": "object", - "properties": { - "owner": { - "description": "The contract's current owner. `None` if the ownership has been renounced.", - "anyOf": [ - { - "$ref": "#/definitions/Addr" - }, - { - "type": "null" - } - ] - }, - "pending_expiry": { - "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "pending_owner": { - "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", - "anyOf": [ - { - "$ref": "#/definitions/Addr" - }, - { - "type": "null" - } - ] } }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "additionalProperties": false }, "get_minter_ownership": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/cw721-expiration/src/contract_tests.rs b/contracts/cw721-expiration/src/contract_tests.rs index bcdee3a98..f6a7bf834 100644 --- a/contracts/cw721-expiration/src/contract_tests.rs +++ b/contracts/cw721-expiration/src/contract_tests.rs @@ -12,7 +12,7 @@ use cw721::msg::{ OwnerOfResponse, TokensResponse, }; use cw721::receiver::Cw721ReceiveMsg; -use cw721::state::{CollectionInfo, DefaultOptionCollectionInfoExtension, CREATOR, MINTER}; +use cw721::state::{CollectionInfo, MINTER}; use cw721::{query::Cw721Query, Approval, Expiration}; use cw_ownable::{Action, Ownership, OwnershipError}; @@ -29,26 +29,14 @@ const SYMBOL: &str = "MGK"; fn setup_contract( deps: DepsMut<'_>, expiration_days: u16, -) -> Cw721ExpirationContract< - 'static, - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, -> { - let contract = Cw721ExpirationContract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); +) -> Cw721ExpirationContract<'static, DefaultOptionMetadataExtension, Empty, Empty> { + let contract = + Cw721ExpirationContract::::default(); let msg = InstantiateMsg { expiration_days, name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - collection_info_extension: None, minter: Some(String::from(MINTER_ADDR)), - creator: Some(String::from(CREATOR_ADDR)), withdraw_address: None, }; let info = mock_info("creator", &[]); @@ -60,20 +48,14 @@ fn setup_contract( #[test] fn proper_instantiation() { let mut deps = mock_dependencies(); - let contract = Cw721ExpirationContract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - Option, - >::default(); + let contract = + Cw721ExpirationContract::::default(); let msg = InstantiateMsg { expiration_days: 1, name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - collection_info_extension: Some(Empty {}), minter: Some(String::from(MINTER_ADDR)), - creator: Some(String::from(CREATOR_ADDR)), withdraw_address: Some(String::from(CREATOR_ADDR)), }; let info = mock_info("creator", &[]); @@ -88,8 +70,6 @@ fn proper_instantiation() { // it worked, let's query the state let minter_ownership = MINTER.get_ownership(deps.as_ref().storage).unwrap(); assert_eq!(Some(Addr::unchecked(MINTER_ADDR)), minter_ownership.owner); - let creator_ownership = CREATOR.get_ownership(deps.as_ref().storage).unwrap(); - assert_eq!(Some(Addr::unchecked(CREATOR_ADDR)), creator_ownership.owner); let collection_info = contract .base_contract .query_collection_info(deps.as_ref(), env.clone()) @@ -99,8 +79,6 @@ fn proper_instantiation() { CollectionInfo { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - extension: Some(Empty {}), - updated_at: env.block.time, } ); @@ -114,7 +92,7 @@ fn proper_instantiation() { let count = contract .base_contract - .query_num_tokens(deps.as_ref(), env.clone()) + .query_num_tokens(deps.as_ref(), env) .unwrap(); assert_eq!(0, count.count); @@ -128,20 +106,14 @@ fn proper_instantiation() { #[test] fn proper_instantiation_with_collection_info() { let mut deps = mock_dependencies(); - let contract = Cw721ExpirationContract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - Option, - >::default(); + let contract = + Cw721ExpirationContract::::default(); let msg = InstantiateMsg { expiration_days: 1, name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - collection_info_extension: Some(Empty {}), minter: Some(String::from(MINTER_ADDR)), - creator: Some(String::from(CREATOR_ADDR)), withdraw_address: Some(String::from(CREATOR_ADDR)), }; let info = mock_info("creator", &[]); @@ -156,8 +128,6 @@ fn proper_instantiation_with_collection_info() { // it worked, let's query the state let minter_ownership = MINTER.get_ownership(deps.as_ref().storage).unwrap(); assert_eq!(Some(Addr::unchecked(MINTER_ADDR)), minter_ownership.owner); - let creator_ownership = CREATOR.get_ownership(deps.as_ref().storage).unwrap(); - assert_eq!(Some(Addr::unchecked(CREATOR_ADDR)), creator_ownership.owner); let collection_info = contract .base_contract .query_collection_info(deps.as_ref(), env.clone()) @@ -167,8 +137,6 @@ fn proper_instantiation_with_collection_info() { CollectionInfo { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - extension: Some(Empty {}), - updated_at: env.block.time, } ); @@ -182,7 +150,7 @@ fn proper_instantiation_with_collection_info() { let count = contract .base_contract - .query_num_tokens(deps.as_ref(), env.clone()) + .query_num_tokens(deps.as_ref(), env) .unwrap(); assert_eq!(0, count.count); @@ -228,7 +196,7 @@ fn test_mint() { // ensure num tokens increases let count = contract .base_contract - .query_num_tokens(deps.as_ref(), env.clone()) + .query_num_tokens(deps.as_ref(), env) .unwrap(); assert_eq!(1, count.count); @@ -324,7 +292,7 @@ fn test_update_minter() { deps.as_mut(), mock_env(), minter_info.clone(), - Cw721ExecuteMsg::UpdateMinterOwnership(Action::TransferOwnership { + Cw721ExecuteMsg::UpdateOwnership(Action::TransferOwnership { new_owner: "random".to_string(), expiry: None, }), @@ -356,7 +324,7 @@ fn test_update_minter() { deps.as_mut(), mock_env(), random_info.clone(), - Cw721ExecuteMsg::UpdateMinterOwnership(Action::AcceptOwnership), + Cw721ExecuteMsg::UpdateOwnership(Action::AcceptOwnership), ) .unwrap(); diff --git a/contracts/cw721-expiration/src/execute.rs b/contracts/cw721-expiration/src/execute.rs index 56ab2c524..8b573a35a 100644 --- a/contracts/cw721-expiration/src/execute.rs +++ b/contracts/cw721-expiration/src/execute.rs @@ -12,25 +12,12 @@ use crate::{ CONTRACT_VERSION, }; -impl< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > - Cw721ExpirationContract< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> + Cw721ExpirationContract<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { // -- instantiate -- pub fn instantiate( @@ -38,7 +25,7 @@ where deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result, ContractError> { if msg.expiration_days == 0 { return Err(ContractError::MinExpiration {}); @@ -47,7 +34,6 @@ where TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg, - TCollectionInfoExtension, >::default(); contract .expiration_days @@ -59,9 +45,7 @@ where Cw721InstantiateMsg { name: msg.name, symbol: msg.symbol, - collection_info_extension: msg.collection_info_extension, minter: msg.minter, - creator: msg.creator, withdraw_address: msg.withdraw_address, }, CONTRACT_NAME, @@ -75,13 +59,12 @@ where deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721ExecuteMsg, + msg: Cw721ExecuteMsg, ) -> Result, ContractError> { let contract = Cw721ExpirationContract::< TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg, - TCollectionInfoExtension, >::default(); match msg { Cw721ExecuteMsg::Mint { diff --git a/contracts/cw721-expiration/src/lib.rs b/contracts/cw721-expiration/src/lib.rs index 93cc11ab2..cb0b1256d 100644 --- a/contracts/cw721-expiration/src/lib.rs +++ b/contracts/cw721-expiration/src/lib.rs @@ -30,10 +30,7 @@ pub mod entry { #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response}; - use cw721::{ - msg::Cw721ExecuteMsg, - state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, - }; + use cw721::{msg::Cw721ExecuteMsg, state::DefaultOptionMetadataExtension}; // This makes a conscious choice on the various generics used by the contract #[cfg_attr(not(feature = "library"), entry_point)] @@ -41,14 +38,10 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { - let contract = Cw721ExpirationContract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = + Cw721ExpirationContract::::default(); contract.instantiate(deps, env, info, msg) } @@ -57,18 +50,10 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721ExecuteMsg< - DefaultOptionMetadataExtension, - Empty, - DefaultOptionCollectionInfoExtension, - >, + msg: Cw721ExecuteMsg, ) -> Result { - let contract = Cw721ExpirationContract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = + Cw721ExpirationContract::::default(); contract.execute(deps, env, info, msg) } @@ -76,14 +61,10 @@ pub mod entry { pub fn query( deps: Deps, env: Env, - msg: QueryMsg, + msg: QueryMsg, ) -> Result { - let contract = Cw721ExpirationContract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = + Cw721ExpirationContract::::default(); contract.query(deps, env, msg) } @@ -98,7 +79,6 @@ pub mod entry { mod tests { use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cw2::ContractVersion; - use cw721::state::DefaultOptionCollectionInfoExtension; use crate::{error::ContractError, msg::InstantiateMsg, state::Cw721ExpirationContract}; @@ -117,9 +97,7 @@ mod tests { expiration_days: 0, name: "collection_name".into(), symbol: "collection_symbol".into(), - collection_info_extension: None, minter: Some("minter".into()), - creator: Some("creator".into()), withdraw_address: None, }, ) @@ -135,9 +113,7 @@ mod tests { expiration_days: 1, name: "".into(), symbol: "".into(), - collection_info_extension: None, minter: Some("minter".into()), - creator: Some("creator".into()), withdraw_address: None, }, ) @@ -154,15 +130,10 @@ mod tests { assert_eq!( 1, - Cw721ExpirationContract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default() - .expiration_days - .load(deps.as_ref().storage) - .unwrap() + Cw721ExpirationContract::::default() + .expiration_days + .load(deps.as_ref().storage) + .unwrap() ); } } diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index 77367577e..5f5d35a2e 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -5,7 +5,7 @@ use cw721::state::CollectionInfo; use cw_ownable::Ownership; #[cw_serde] -pub struct InstantiateMsg { +pub struct InstantiateMsg { /// max 65535 days pub expiration_days: u16, @@ -15,22 +15,17 @@ pub struct InstantiateMsg { /// Symbol of the NFT contract pub symbol: String, - pub collection_info_extension: TCollectionInfoExtension, - /// The minter is the only one who can create new NFTs. /// This is designed for a base NFT that is controlled by an external program /// or contract. You will likely replace this with custom logic in custom NFTs pub minter: Option, - /// The creator is the only who can update collection info. - pub creator: Option, - pub withdraw_address: Option, } #[cw_serde] #[derive(QueryResponses)] -pub enum QueryMsg { +pub enum QueryMsg { // -------- below adds `include_expired_nft` prop to cw721/src/msg.rs -------- /// Return the owner of the given token, error if token does not exist #[returns(cw721::msg::OwnerOfResponse)] @@ -123,27 +118,21 @@ pub enum QueryMsg { #[returns(cw721::msg::NumTokensResponse)] NumTokens {}, - #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] - #[returns(cw721::state::CollectionInfo)] + #[returns(cw721::state::CollectionInfo)] ContractInfo {}, /// With MetaData Extension. /// Returns top-level metadata about the contract - #[returns(CollectionInfo)] + #[returns(CollectionInfo)] GetCollectionInfo {}, - #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] #[returns(Ownership)] Ownership {}, #[returns(Ownership)] GetMinterOwnership {}, - #[returns(Ownership)] - GetCreatorOwnership {}, - /// Return the minter - #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] #[returns(MinterResponse)] Minter {}, @@ -151,11 +140,6 @@ pub enum QueryMsg { #[returns(())] Extension { msg: TMetadataExtension }, - /// This is a workaround and dummy query like (same as for Extension) for avoiding this compiler error: - /// `cannot infer type for type parameter `TCollectionInfoExtension` declared on the enum `QueryMsg` - #[returns(())] - GetCollectionInfoExtension { msg: TCollectionInfoExtension }, - #[returns(Option)] GetWithdrawAddress {}, } diff --git a/contracts/cw721-expiration/src/query.rs b/contracts/cw721-expiration/src/query.rs index f921eccf2..552b051ed 100644 --- a/contracts/cw721-expiration/src/query.rs +++ b/contracts/cw721-expiration/src/query.rs @@ -9,37 +9,23 @@ use serde::Serialize; use crate::{error::ContractError, msg::QueryMsg, state::Cw721ExpirationContract}; -impl< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > - Cw721ExpirationContract< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> + Cw721ExpirationContract<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub fn query( &self, deps: Deps, env: Env, - msg: QueryMsg, + msg: QueryMsg, ) -> Result { let contract = Cw721ExpirationContract::< TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg, - TCollectionInfoExtension, >::default(); match msg { // -------- msgs with `include_expired_nft` prop -------- @@ -164,14 +150,12 @@ where QueryMsg::NumTokens {} => Ok(to_json_binary( &contract.base_contract.query_num_tokens(deps, env)?, )?), - #[allow(deprecated)] QueryMsg::ContractInfo {} => Ok(to_json_binary( &contract.base_contract.query_collection_info(deps, env)?, )?), QueryMsg::GetCollectionInfo {} => Ok(to_json_binary( &contract.base_contract.query_collection_info(deps, env)?, )?), - #[allow(deprecated)] QueryMsg::Ownership {} => Ok(to_json_binary( &contract .base_contract @@ -182,23 +166,12 @@ where .base_contract .query_minter_ownership(deps.storage)?, )?), - QueryMsg::GetCreatorOwnership {} => Ok(to_json_binary( - &contract - .base_contract - .query_creator_ownership(deps.storage)?, - )?), - #[allow(deprecated)] QueryMsg::Minter {} => Ok(to_json_binary( &contract.base_contract.query_minter(deps.storage)?, )?), QueryMsg::Extension { msg } => Ok(to_json_binary( &contract.base_contract.query_extension(deps, env, msg)?, )?), - QueryMsg::GetCollectionInfoExtension { msg } => Ok(to_json_binary( - &contract - .base_contract - .query_collection_info_extension(deps, env, msg)?, - )?), QueryMsg::GetWithdrawAddress {} => Ok(to_json_binary( &contract.base_contract.query_withdraw_address(deps)?, )?), diff --git a/contracts/cw721-expiration/src/state.rs b/contracts/cw721-expiration/src/state.rs index 55b5f8a86..de397b46d 100644 --- a/contracts/cw721-expiration/src/state.rs +++ b/contracts/cw721-expiration/src/state.rs @@ -12,41 +12,26 @@ pub struct Cw721ExpirationContract< TCustomResponseMessage, // Message passed for updating metadata. TMetadataExtensionMsg, - // Extension defined in CollectionInfo. - TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub expiration_days: Item<'a, u16>, // max 65535 days pub mint_timestamps: Map<'a, &'a str, Timestamp>, - pub base_contract: Cw721Contract< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - >, + pub base_contract: + Cw721Contract<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg>, } -impl< - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > Default +impl Default for Cw721ExpirationContract< 'static, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg, - TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { Self { diff --git a/contracts/cw721-fixed-price/examples/schema.rs b/contracts/cw721-fixed-price/examples/schema.rs index 4ad3733e7..4825e2b24 100644 --- a/contracts/cw721-fixed-price/examples/schema.rs +++ b/contracts/cw721-fixed-price/examples/schema.rs @@ -1,11 +1,10 @@ use cosmwasm_schema::write_api; -use cw721::state::DefaultOptionCollectionInfoExtension; use cw721_fixed_price::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; fn main() { write_api! { - instantiate: InstantiateMsg, + instantiate: InstantiateMsg, execute: ExecuteMsg, query: QueryMsg, } diff --git a/contracts/cw721-fixed-price/schema/cw721-fixed-price.json b/contracts/cw721-fixed-price/schema/cw721-fixed-price.json index 251684b7c..296c736a8 100644 --- a/contracts/cw721-fixed-price/schema/cw721-fixed-price.json +++ b/contracts/cw721-fixed-price/schema/cw721-fixed-price.json @@ -17,16 +17,6 @@ "unit_price" ], "properties": { - "collection_info_extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, "cw20_address": { "$ref": "#/definitions/Addr" }, @@ -78,58 +68,6 @@ "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", "type": "string" }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, "Metadata": { "type": "object", "properties": { @@ -193,30 +131,6 @@ }, "additionalProperties": false }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, "Trait": { "type": "object", "required": [ @@ -242,10 +156,6 @@ "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", "type": "string" - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" } } }, diff --git a/contracts/cw721-fixed-price/src/contract.rs b/contracts/cw721-fixed-price/src/contract.rs index 558ece937..da17f9c61 100644 --- a/contracts/cw721-fixed-price/src/contract.rs +++ b/contracts/cw721-fixed-price/src/contract.rs @@ -13,7 +13,7 @@ use cw2::set_contract_version; use cw20::Cw20ReceiveMsg; use cw721::helpers::Cw721Contract; use cw721::msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}; -use cw721::state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}; +use cw721::state::DefaultOptionMetadataExtension; use cw_utils::parse_reply_instantiate_data; // version info for migration info @@ -27,7 +27,7 @@ pub fn instantiate( deps: DepsMut, _env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -60,9 +60,7 @@ pub fn instantiate( msg: to_json_binary(&Cw721InstantiateMsg { name: msg.name.clone(), symbol: msg.symbol, - collection_info_extension: msg.collection_info_extension, minter: None, - creator: None, withdraw_address: msg.withdraw_address, })?, funds: vec![], @@ -161,7 +159,7 @@ pub fn execute_receive( return Err(ContractError::WrongPaymentAmount {}); } - let mint_msg = Cw721ExecuteMsg::::Mint { + let mint_msg = Cw721ExecuteMsg::::Mint { token_id: config.unused_token_id.to_string(), owner: sender, token_uri: config.token_uri.clone().into(), @@ -170,11 +168,10 @@ pub fn execute_receive( match config.cw721_address.clone() { Some(cw721) => { - let callback = Cw721Contract::( + let callback = Cw721Contract::( cw721, PhantomData, PhantomData, - PhantomData, ) .call(mint_msg)?; config.unused_token_id += 1; @@ -214,7 +211,6 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), - collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -235,9 +231,7 @@ mod tests { msg: to_json_binary(&Cw721InstantiateMsg { name: msg.name.clone(), symbol: msg.symbol.clone(), - collection_info_extension: msg.collection_info_extension, minter: None, - creator: None, withdraw_address: None, }) .unwrap(), @@ -300,7 +294,6 @@ mod tests { unit_price: Uint128::new(0), name: String::from("SYNTH"), symbol: String::from("SYNTH"), - collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -326,7 +319,6 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), - collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -352,7 +344,6 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), - collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -390,7 +381,7 @@ mod tests { let info = mock_info(MOCK_CONTRACT_ADDR, &[]); let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - let mint_msg = Cw721ExecuteMsg::::Mint { + let mint_msg = Cw721ExecuteMsg::::Mint { token_id: String::from("0"), owner: String::from("minter"), token_uri: Some(String::from("https://ipfs.io/ipfs/Q")), @@ -421,7 +412,6 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), - collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -464,7 +454,6 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), - collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -509,7 +498,6 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), - collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -565,7 +553,6 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), - collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -601,7 +588,6 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), - collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), @@ -657,7 +643,6 @@ mod tests { unit_price: Uint128::new(1), name: String::from("SYNTH"), symbol: String::from("SYNTH"), - collection_info_extension: None, token_code_id: 10u64, cw20_address: Addr::unchecked(MOCK_CONTRACT_ADDR), token_uri: String::from("https://ipfs.io/ipfs/Q"), diff --git a/contracts/cw721-fixed-price/src/msg.rs b/contracts/cw721-fixed-price/src/msg.rs index a27fb23b5..ae9745b43 100644 --- a/contracts/cw721-fixed-price/src/msg.rs +++ b/contracts/cw721-fixed-price/src/msg.rs @@ -4,7 +4,7 @@ use cw20::Cw20ReceiveMsg; use cw721::state::DefaultOptionMetadataExtension; #[cw_serde] -pub struct InstantiateMsg { +pub struct InstantiateMsg { pub owner: Addr, pub max_tokens: u32, pub unit_price: Uint128, @@ -14,7 +14,6 @@ pub struct InstantiateMsg { pub cw20_address: Addr, pub token_uri: String, pub extension: DefaultOptionMetadataExtension, - pub collection_info_extension: TCollectionInfoExtension, pub withdraw_address: Option, } diff --git a/contracts/cw721-non-transferable/examples/schema.rs b/contracts/cw721-non-transferable/examples/schema.rs index 67b019b81..e3b3fffb2 100644 --- a/contracts/cw721-non-transferable/examples/schema.rs +++ b/contracts/cw721-non-transferable/examples/schema.rs @@ -9,9 +9,8 @@ use cw721::{ AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, Cw721ExecuteMsg, MinterResponse, NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }, - state::{CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, + state::{CollectionInfo, DefaultOptionMetadataExtension}, }; -#[allow(deprecated)] use cw721_non_transferable::{InstantiateMsg, QueryMsg}; fn main() { @@ -20,13 +19,9 @@ fn main() { create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); + export_schema_with_title(&schema_for!(InstantiateMsg), &out_dir, "InstantiateMsg"); export_schema_with_title( - &schema_for!(InstantiateMsg), - &out_dir, - "InstantiateMsg", - ); - export_schema_with_title( - &schema_for!(Cw721ExecuteMsg), + &schema_for!(Cw721ExecuteMsg), &out_dir, "ExecuteMsg", ); @@ -44,11 +39,7 @@ fn main() { export_schema(&schema_for!(ApprovalResponse), &out_dir); export_schema(&schema_for!(ApprovalsResponse), &out_dir); export_schema(&schema_for!(OperatorsResponse), &out_dir); - export_schema_with_title( - &schema_for!(CollectionInfo), - &out_dir, - "CollectionInfo", - ); + export_schema_with_title(&schema_for!(CollectionInfo), &out_dir, "CollectionInfo"); export_schema(&schema_for!(MinterResponse), &out_dir); export_schema(&schema_for!(NumTokensResponse), &out_dir); export_schema(&schema_for!(OwnerOfResponse), &out_dir); diff --git a/contracts/cw721-non-transferable/schema/collection_info.json b/contracts/cw721-non-transferable/schema/collection_info.json index bae78d23a..ebb8e6e8e 100644 --- a/contracts/cw721-non-transferable/schema/collection_info.json +++ b/contracts/cw721-non-transferable/schema/collection_info.json @@ -4,115 +4,15 @@ "type": "object", "required": [ "name", - "symbol", - "updated_at" + "symbol" ], "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, "name": { "type": "string" }, "symbol": { "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "additionalProperties": false } diff --git a/contracts/cw721-non-transferable/schema/execute_msg.json b/contracts/cw721-non-transferable/schema/execute_msg.json index 549baff69..2bcf2fea7 100644 --- a/contracts/cw721-non-transferable/schema/execute_msg.json +++ b/contracts/cw721-non-transferable/schema/execute_msg.json @@ -3,7 +3,6 @@ "title": "ExecuteMsg", "oneOf": [ { - "deprecated": true, "type": "object", "required": [ "update_ownership" @@ -15,52 +14,6 @@ }, "additionalProperties": false }, - { - "type": "object", - "required": [ - "update_minter_ownership" - ], - "properties": { - "update_minter_ownership": { - "$ref": "#/definitions/Action" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "update_creator_ownership" - ], - "properties": { - "update_creator_ownership": { - "$ref": "#/definitions/Action" - } - }, - "additionalProperties": false - }, - { - "description": "Update the collection, can only called by the contract creator", - "type": "object", - "required": [ - "update_collection_info" - ], - "properties": { - "update_collection_info": { - "type": "object", - "required": [ - "collection_info" - ], - "properties": { - "collection_info": { - "$ref": "#/definitions/CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "description": "Transfer is a base message to move a token to another account without triggering actions", "type": "object", @@ -434,10 +387,6 @@ } ] }, - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, "Binary": { "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" @@ -457,84 +406,6 @@ } } }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "name", - "symbol" - ], - "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, "Empty": { "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", "type": "object" @@ -649,22 +520,6 @@ }, "additionalProperties": false }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, "Timestamp": { "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", "allOf": [ diff --git a/contracts/cw721-non-transferable/schema/instantiate_msg.json b/contracts/cw721-non-transferable/schema/instantiate_msg.json index cbffb9324..cb5c43020 100644 --- a/contracts/cw721-non-transferable/schema/instantiate_msg.json +++ b/contracts/cw721-non-transferable/schema/instantiate_msg.json @@ -13,22 +13,6 @@ "null" ] }, - "collection_info_extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "creator": { - "type": [ - "string", - "null" - ] - }, "minter": { "type": [ "string", @@ -48,91 +32,5 @@ ] } }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "additionalProperties": false } diff --git a/contracts/cw721-non-transferable/schema/minter_response.json b/contracts/cw721-non-transferable/schema/minter_response.json index e79df37e8..3d450621a 100644 --- a/contracts/cw721-non-transferable/schema/minter_response.json +++ b/contracts/cw721-non-transferable/schema/minter_response.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "MinterResponse", - "description": "Shows who can mint these tokens", + "description": "Deprecated: use Cw721QueryMsg::GetMinterOwnership instead! Shows who can mint these tokens.", "type": "object", "properties": { "minter": { diff --git a/contracts/cw721-non-transferable/schema/query_msg.json b/contracts/cw721-non-transferable/schema/query_msg.json index 4c2d624d3..4cde7f9f8 100644 --- a/contracts/cw721-non-transferable/schema/query_msg.json +++ b/contracts/cw721-non-transferable/schema/query_msg.json @@ -155,7 +155,6 @@ "additionalProperties": false }, { - "deprecated": true, "type": "object", "required": [ "contract_info" @@ -168,45 +167,6 @@ }, "additionalProperties": false }, - { - "type": "object", - "required": [ - "get_collection_info" - ], - "properties": { - "get_collection_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_minter_ownership" - ], - "properties": { - "get_minter_ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_creator_ownership" - ], - "properties": { - "get_creator_ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "type": "object", "required": [ @@ -320,7 +280,6 @@ "additionalProperties": false }, { - "deprecated": true, "type": "object", "required": [ "minter" diff --git a/contracts/cw721-non-transferable/src/lib.rs b/contracts/cw721-non-transferable/src/lib.rs index da684ff1c..c68c81981 100644 --- a/contracts/cw721-non-transferable/src/lib.rs +++ b/contracts/cw721-non-transferable/src/lib.rs @@ -1,6 +1,6 @@ pub use crate::msg::{InstantiateMsg, QueryMsg}; use cosmwasm_std::Empty; -use cw721::state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}; +use cw721::state::DefaultOptionMetadataExtension; pub use cw721_base::{ entry::{execute as _execute, query as _query}, Cw721Contract, @@ -14,13 +14,8 @@ pub mod state; const CONTRACT_NAME: &str = "crates.io:cw721-non-transferable"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -pub type Cw721NonTransferableContract<'a> = Cw721Contract< - 'a, - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, ->; +pub type Cw721NonTransferableContract<'a> = + Cw721Contract<'a, DefaultOptionMetadataExtension, Empty, Empty>; #[cfg(not(feature = "library"))] pub mod entry { @@ -40,7 +35,7 @@ pub mod entry { mut deps: DepsMut, env: Env, info: MessageInfo, - msg: InstantiateMsg, + msg: InstantiateMsg, ) -> Result { let admin_addr: Option = msg .admin @@ -55,9 +50,7 @@ pub mod entry { let cw721_base_instantiate_msg = Cw721InstantiateMsg { name: msg.name, symbol: msg.symbol, - collection_info_extension: msg.collection_info_extension, minter: msg.minter, - creator: msg.creator, withdraw_address: msg.withdraw_address, }; @@ -82,11 +75,7 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721ExecuteMsg< - DefaultOptionMetadataExtension, - Empty, - DefaultOptionCollectionInfoExtension, - >, + msg: Cw721ExecuteMsg, ) -> Result { let config = CONFIG.load(deps.storage)?; match config.admin { diff --git a/contracts/cw721-non-transferable/src/msg.rs b/contracts/cw721-non-transferable/src/msg.rs index 59c461997..1525d5b32 100644 --- a/contracts/cw721-non-transferable/src/msg.rs +++ b/contracts/cw721-non-transferable/src/msg.rs @@ -1,17 +1,12 @@ use cosmwasm_schema::cw_serde; -use cw721::{ - msg::Cw721QueryMsg, - state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, -}; +use cw721::{msg::Cw721QueryMsg, state::DefaultOptionMetadataExtension}; #[cw_serde] -pub struct InstantiateMsg { +pub struct InstantiateMsg { pub admin: Option, pub name: String, pub symbol: String, - pub collection_info_extension: TCollectionInfoExtension, pub minter: Option, - pub creator: Option, pub withdraw_address: Option, } @@ -40,11 +35,7 @@ pub enum QueryMsg { limit: Option, }, NumTokens {}, - #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] ContractInfo {}, - GetCollectionInfo {}, - GetMinterOwnership {}, - GetCreatorOwnership {}, NftInfo { token_id: String, @@ -62,18 +53,13 @@ pub enum QueryMsg { start_after: Option, limit: Option, }, - #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] Minter {}, GetWithdrawAddress {}, } -impl From - for Cw721QueryMsg -{ - fn from( - msg: QueryMsg, - ) -> Cw721QueryMsg { +impl From for Cw721QueryMsg { + fn from(msg: QueryMsg) -> Cw721QueryMsg { match msg { QueryMsg::OwnerOf { token_id, @@ -83,9 +69,7 @@ impl From include_expired, }, QueryMsg::NumTokens {} => Cw721QueryMsg::NumTokens {}, - #[allow(deprecated)] - QueryMsg::ContractInfo {} => Cw721QueryMsg::GetCollectionInfo {}, - QueryMsg::GetCollectionInfo {} => Cw721QueryMsg::GetCollectionInfo {}, + QueryMsg::ContractInfo {} => Cw721QueryMsg::ContractInfo {}, QueryMsg::NftInfo { token_id } => Cw721QueryMsg::NftInfo { token_id }, QueryMsg::AllNftInfo { token_id, @@ -106,10 +90,7 @@ impl From QueryMsg::AllTokens { start_after, limit } => { Cw721QueryMsg::AllTokens { start_after, limit } } - #[allow(deprecated)] QueryMsg::Minter {} => Cw721QueryMsg::Minter {}, - QueryMsg::GetMinterOwnership {} => Cw721QueryMsg::GetMinterOwnership {}, - QueryMsg::GetCreatorOwnership {} => Cw721QueryMsg::GetCreatorOwnership {}, QueryMsg::GetWithdrawAddress {} => Cw721QueryMsg::GetWithdrawAddress {}, QueryMsg::AllOperators { .. } => unreachable!("AllOperators is not supported!"), QueryMsg::Approval { .. } => unreachable!("Approval is not supported!"), diff --git a/contracts/cw721-receiver-tester/tests/multitest.rs b/contracts/cw721-receiver-tester/tests/multitest.rs index 3c074a908..a3c3b251d 100644 --- a/contracts/cw721-receiver-tester/tests/multitest.rs +++ b/contracts/cw721-receiver-tester/tests/multitest.rs @@ -1,5 +1,4 @@ use cosmwasm_std::{to_json_binary, Addr, Attribute, Binary}; -use cw721::state::DefaultOptionCollectionInfoExtension; use cw_multi_test::{App, ContractWrapper, Executor}; #[test] @@ -20,7 +19,7 @@ fn test_cw721_base_receive_succeed() { .execute_contract( admin.clone(), nft_contract, - &ExecuteMsg::<(), (), ()>::SendNft { + &ExecuteMsg::<(), ()>::SendNft { contract: receiver_contract.to_string(), token_id: "test".to_string(), msg: to_json_binary(&InnerMsg::Succeed).unwrap(), @@ -76,7 +75,7 @@ fn test_cw721_base_receive_fail() { let result = app.execute_contract( admin.clone(), nft_contract.clone(), - &ExecuteMsg::<(), (), ()>::SendNft { + &ExecuteMsg::<(), ()>::SendNft { contract: receiver_contract.to_string(), token_id: "test".to_string(), msg: to_json_binary(&InnerMsg::Fail).unwrap(), @@ -89,7 +88,7 @@ fn test_cw721_base_receive_fail() { let result = app.execute_contract( admin, nft_contract, - &ExecuteMsg::<(), (), ()>::SendNft { + &ExecuteMsg::<(), ()>::SendNft { contract: receiver_contract.to_string(), token_id: "test".to_string(), msg: Binary::from(br#"{"invalid": "fields"}"#), @@ -123,12 +122,10 @@ fn setup_contracts(app: &mut App, admin: Addr) -> Contracts { .instantiate_contract( nft_code_id, admin.clone(), - &base_msg::InstantiateMsg:: { + &base_msg::InstantiateMsg { name: "nft".to_string(), symbol: "NFT".to_string(), - collection_info_extension: None, minter: Some(admin.to_string()), - creator: Some(admin.to_string()), withdraw_address: None, }, &[], @@ -152,7 +149,7 @@ fn setup_contracts(app: &mut App, admin: Addr) -> Contracts { app.execute_contract( admin.clone(), nft_contract.clone(), - &base_msg::ExecuteMsg::<(), (), ()>::Mint { + &base_msg::ExecuteMsg::<(), ()>::Mint { token_id: "test".to_string(), owner: admin.to_string(), token_uri: Some("https://example.com".to_string()), diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 6d0f00017..659b782da 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -11,7 +11,7 @@ use cw721::{ OperatorsResponse, OwnerOfResponse, TokensResponse, }, receiver::Cw721ReceiveMsg, - state::{CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, + state::{CollectionInfo, DefaultOptionMetadataExtension}, }; fn main() { let mut out_dir = current_dir().unwrap(); @@ -20,26 +20,16 @@ fn main() { remove_schemas(&out_dir).unwrap(); export_schema_with_title( - &schema_for!(Cw721InstantiateMsg), + &schema_for!(Cw721InstantiateMsg), &out_dir, "InstantiateMsg", ); export_schema_with_title( - &schema_for!( - Cw721ExecuteMsg::< - DefaultOptionMetadataExtension, - Empty, - DefaultOptionCollectionInfoExtension, - > - ), + &schema_for!(Cw721ExecuteMsg::), &out_dir, "ExecuteMsg", ); - export_schema_with_title( - &schema_for!(Cw721QueryMsg), - &out_dir, - "QueryMsg", - ); + export_schema_with_title(&schema_for!(Cw721QueryMsg), &out_dir, "QueryMsg"); export_schema(&schema_for!(Cw721ReceiveMsg), &out_dir); export_schema_with_title( &schema_for!(NftInfoResponse), @@ -55,11 +45,7 @@ fn main() { export_schema(&schema_for!(ApprovalsResponse), &out_dir); export_schema(&schema_for!(OperatorResponse), &out_dir); export_schema(&schema_for!(OperatorsResponse), &out_dir); - export_schema_with_title( - &schema_for!(CollectionInfo), - &out_dir, - "CollectionInfo", - ); + export_schema_with_title(&schema_for!(CollectionInfo), &out_dir, "CollectionInfo"); export_schema(&schema_for!(OwnerOfResponse), &out_dir); export_schema(&schema_for!(NumTokensResponse), &out_dir); export_schema(&schema_for!(TokensResponse), &out_dir); diff --git a/packages/cw721/schema/collection_info.json b/packages/cw721/schema/collection_info.json index bae78d23a..ebb8e6e8e 100644 --- a/packages/cw721/schema/collection_info.json +++ b/packages/cw721/schema/collection_info.json @@ -4,115 +4,15 @@ "type": "object", "required": [ "name", - "symbol", - "updated_at" + "symbol" ], "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, "name": { "type": "string" }, "symbol": { "type": "string" - }, - "updated_at": { - "$ref": "#/definitions/Timestamp" } }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "additionalProperties": false } diff --git a/packages/cw721/schema/execute_msg.json b/packages/cw721/schema/execute_msg.json index 549baff69..2bcf2fea7 100644 --- a/packages/cw721/schema/execute_msg.json +++ b/packages/cw721/schema/execute_msg.json @@ -3,7 +3,6 @@ "title": "ExecuteMsg", "oneOf": [ { - "deprecated": true, "type": "object", "required": [ "update_ownership" @@ -15,52 +14,6 @@ }, "additionalProperties": false }, - { - "type": "object", - "required": [ - "update_minter_ownership" - ], - "properties": { - "update_minter_ownership": { - "$ref": "#/definitions/Action" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "update_creator_ownership" - ], - "properties": { - "update_creator_ownership": { - "$ref": "#/definitions/Action" - } - }, - "additionalProperties": false - }, - { - "description": "Update the collection, can only called by the contract creator", - "type": "object", - "required": [ - "update_collection_info" - ], - "properties": { - "update_collection_info": { - "type": "object", - "required": [ - "collection_info" - ], - "properties": { - "collection_info": { - "$ref": "#/definitions/CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "description": "Transfer is a base message to move a token to another account without triggering actions", "type": "object", @@ -434,10 +387,6 @@ } ] }, - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, "Binary": { "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", "type": "string" @@ -457,84 +406,6 @@ } } }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "CollectionInfoMsg_for_Nullable_CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "name", - "symbol" - ], - "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, "Empty": { "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", "type": "object" @@ -649,22 +520,6 @@ }, "additionalProperties": false }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, "Timestamp": { "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", "allOf": [ diff --git a/packages/cw721/schema/instantiate_msg.json b/packages/cw721/schema/instantiate_msg.json index 660e888ab..1a02553e0 100644 --- a/packages/cw721/schema/instantiate_msg.json +++ b/packages/cw721/schema/instantiate_msg.json @@ -7,23 +7,6 @@ "symbol" ], "properties": { - "collection_info_extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "creator": { - "description": "The creator is the only who can update collection info.", - "type": [ - "string", - "null" - ] - }, "minter": { "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", "type": [ @@ -46,91 +29,5 @@ ] } }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "additionalProperties": false } diff --git a/packages/cw721/schema/query_msg.json b/packages/cw721/schema/query_msg.json index d18b5bc4c..a56350cc7 100644 --- a/packages/cw721/schema/query_msg.json +++ b/packages/cw721/schema/query_msg.json @@ -181,7 +181,6 @@ "additionalProperties": false }, { - "deprecated": true, "type": "object", "required": [ "contract_info" @@ -195,21 +194,6 @@ "additionalProperties": false }, { - "description": "With MetaData Extension. Returns top-level metadata about the contract", - "type": "object", - "required": [ - "get_collection_info" - ], - "properties": { - "get_collection_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "deprecated": true, "type": "object", "required": [ "ownership" @@ -222,32 +206,6 @@ }, "additionalProperties": false }, - { - "type": "object", - "required": [ - "get_minter_ownership" - ], - "properties": { - "get_minter_ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_creator_ownership" - ], - "properties": { - "get_creator_ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", "type": "object", @@ -367,7 +325,6 @@ }, { "description": "Return the minter", - "deprecated": true, "type": "object", "required": [ "minter" @@ -414,122 +371,12 @@ } }, "additionalProperties": false - }, - { - "description": "Do not use - dummy collection info extension query, needed for inferring type parameter during compile", - "type": "object", - "required": [ - "get_collection_info_extension" - ], - "properties": { - "get_collection_info_extension": { - "type": "object", - "properties": { - "msg": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionInfoExtension_for_RoyaltyInfo" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false } ], "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "CollectionInfoExtension_for_RoyaltyInfo": { - "type": "object", - "required": [ - "description", - "image" - ], - "properties": { - "description": { - "type": "string" - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": "string" - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfo" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, "Empty": { "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", "type": "object" - }, - "RoyaltyInfo": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "$ref": "#/definitions/Addr" - }, - "share": { - "$ref": "#/definitions/Decimal" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" } } } diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index a21676ab2..f65bc5623 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -2,7 +2,7 @@ use cosmwasm_std::{ Addr, Api, BankMsg, Binary, Coin, CustomMsg, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, Storage, }; -use cw_ownable::{none_or, Action, Ownership, OwnershipError}; +use cw_ownable::{none_or, Action, Ownership, OwnershipError, OwnershipStore}; use cw_storage_plus::Item; use cw_utils::Expiration; use serde::de::DeserializeOwned; @@ -10,12 +10,9 @@ use serde::Serialize; use crate::{ error::Cw721ContractError, - msg::{CollectionInfoMsg, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg}, + msg::{Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg}, receiver::Cw721ReceiveMsg, - state::{ - CollectionInfo, Cw721Config, DefaultOptionCollectionInfoExtension, - DefaultOptionMetadataExtension, NftInfo, CREATOR, MINTER, - }, + state::{CollectionInfo, Cw721Config, DefaultOptionMetadataExtension, NftInfo, MINTER}, Approval, }; @@ -26,57 +23,41 @@ pub trait Cw721Execute< TCustomResponseMessage, // Message passed for updating metadata. TMetadataExtensionMsg, - // Message passed for updating collection info extension. - TCollectionInfoExtensionMsg, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtensionMsg: Serialize + DeserializeOwned + Clone, { fn instantiate( &self, deps: DepsMut, - env: Env, + _env: Env, info: MessageInfo, - msg: Cw721InstantiateMsg, + msg: Cw721InstantiateMsg, contract_name: &str, contract_version: &str, ) -> Result, Cw721ContractError> { cw2::set_contract_version(deps.storage, contract_name, contract_version)?; - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let collection_info = CollectionInfo { name: msg.name, symbol: msg.symbol, - extension: msg.collection_info_extension, - updated_at: env.block.time, }; config .collection_info .save(deps.storage, &collection_info)?; - // use info.sender if None is passed - let minter: &str = match msg.minter.as_deref() { - Some(minter) => minter, - None => info.sender.as_str(), - }; - self.initialize_minter(deps.storage, deps.api, Some(minter))?; - - // use info.sender if None is passed - let creator: &str = match msg.creator.as_deref() { - Some(creator) => creator, - None => info.sender.as_str(), + let minter = match msg.minter { + Some(owner) => deps.api.addr_validate(&owner)?, + None => info.sender, }; - self.initialize_creator(deps.storage, deps.api, Some(creator))?; + self.initialize_minter(deps.storage, deps.api, Some(minter.as_ref()))?; if let Some(withdraw_address) = msg.withdraw_address { - let creator = deps.api.addr_validate(creator)?; - self.set_withdraw_address(deps, &creator, withdraw_address)?; + self.set_withdraw_address(deps, &minter, withdraw_address)?; } - Ok(Response::default() - .add_attribute("minter", minter) - .add_attribute("creator", creator)) + Ok(Response::default().add_attribute("minter", minter)) } fn execute( @@ -84,16 +65,9 @@ pub trait Cw721Execute< deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721ExecuteMsg< - TMetadataExtension, - TMetadataExtensionMsg, - TCollectionInfoExtensionMsg, - >, + msg: Cw721ExecuteMsg, ) -> Result, Cw721ContractError> { match msg { - Cw721ExecuteMsg::UpdateCollectionInfo { collection_info } => { - self.update_collection_info(deps, info, env, collection_info) - } Cw721ExecuteMsg::Mint { token_id, owner, @@ -122,16 +96,9 @@ pub trait Cw721Execute< msg, } => self.send_nft(deps, env, info, contract, token_id, msg), Cw721ExecuteMsg::Burn { token_id } => self.burn_nft(deps, env, info, token_id), - #[allow(deprecated)] Cw721ExecuteMsg::UpdateOwnership(action) => { self.update_minter_ownership(deps, env, info, action) } - Cw721ExecuteMsg::UpdateMinterOwnership(action) => { - self.update_minter_ownership(deps, env, info, action) - } - Cw721ExecuteMsg::UpdateCreatorOwnership(action) => { - self.update_creator_ownership(deps, env, info, action) - } Cw721ExecuteMsg::Extension { msg } => { self.update_metadata_extension(deps, env, info, msg) } @@ -161,7 +128,6 @@ pub trait Cw721Execute< // ... then migrate let response = migrate_version(deps.storage, contract_name, contract_version, response)?; // ... and update creator and minter AFTER legacy migration - let response = migrate_creator(deps.storage, deps.api, &env, &msg, response)?; let response = migrate_minter(deps.storage, deps.api, &env, &msg, response)?; Ok(response) } @@ -270,7 +236,6 @@ pub trait Cw721Execute< TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg, - TCollectionInfoExtensionMsg, >::default(); config .operators @@ -296,7 +261,6 @@ pub trait Cw721Execute< TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg, - TCollectionInfoExtensionMsg, >::default(); config .operators @@ -319,7 +283,6 @@ pub trait Cw721Execute< TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg, - TCollectionInfoExtensionMsg, >::default(); let token = config.nft_info.load(deps.storage, &token_id)?; check_can_send(deps.as_ref(), &env, &info, &token)?; @@ -334,15 +297,6 @@ pub trait Cw721Execute< } // ------- opionated cw721 functions ------- - fn initialize_creator( - &self, - storage: &mut dyn Storage, - api: &dyn Api, - creator: Option<&str>, - ) -> StdResult> { - CREATOR.initialize_owner(storage, api, creator) - } - fn initialize_minter( &self, storage: &mut dyn Storage, @@ -352,32 +306,6 @@ pub trait Cw721Execute< MINTER.initialize_owner(storage, api, minter) } - fn update_collection_info( - &self, - deps: DepsMut, - info: MessageInfo, - env: Env, - msg: CollectionInfoMsg, - ) -> Result, Cw721ContractError> { - CREATOR.assert_owner(deps.storage, &info.sender)?; - let collection_info = CollectionInfo { - name: msg.name, - symbol: msg.symbol, - extension: msg.extension, - updated_at: env.block.time, - }; - let config = - Cw721Config::::default( - ); - config - .collection_info - .save(deps.storage, &collection_info)?; - - Ok(Response::new() - .add_attribute("action", "update_collection_info") - .add_attribute("sender", info.sender)) - } - fn mint( &self, deps: DepsMut, @@ -396,7 +324,7 @@ pub trait Cw721Execute< token_uri, extension, }; - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); config .nft_info .update(deps.storage, &token_id, |old| match old { @@ -426,19 +354,6 @@ pub trait Cw721Execute< .add_attributes(ownership.into_attributes())) } - fn update_creator_ownership( - &self, - deps: DepsMut, - env: Env, - info: MessageInfo, - action: Action, - ) -> Result, Cw721ContractError> { - let ownership = CREATOR.update_ownership(deps, &env.block, &info.sender, action)?; - Ok(Response::new() - .add_attribute("update_creator_ownership", info.sender) - .add_attributes(ownership.into_attributes())) - } - /// Allows creator to update onchain metadata. For now this is a no-op. fn update_metadata_extension( &self, @@ -447,7 +362,7 @@ pub trait Cw721Execute< info: MessageInfo, _msg: TMetadataExtensionMsg, ) -> Result, Cw721ContractError> { - CREATOR.assert_owner(deps.storage, &info.sender)?; + cw_ownable::assert_owner(deps.storage, &info.sender)?; Ok(Response::new().add_attribute("action", "update_metadata_extension")) } @@ -457,13 +372,12 @@ pub trait Cw721Execute< sender: &Addr, address: String, ) -> Result, Cw721ContractError> { - CREATOR.assert_owner(deps.storage, sender)?; + cw_ownable::assert_owner(deps.storage, sender)?; deps.api.addr_validate(&address)?; let config = Cw721Config::< TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg, - TCollectionInfoExtensionMsg, >::default(); config.withdraw_address.save(deps.storage, &address)?; Ok(Response::new() @@ -476,12 +390,11 @@ pub trait Cw721Execute< storage: &mut dyn Storage, sender: &Addr, ) -> Result, Cw721ContractError> { - CREATOR.assert_owner(storage, sender)?; + cw_ownable::assert_owner(storage, sender)?; let config = Cw721Config::< TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg, - TCollectionInfoExtensionMsg, >::default(); let address = config.withdraw_address.may_load(storage)?; match address { @@ -504,7 +417,6 @@ pub trait Cw721Execute< TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg, - TCollectionInfoExtensionMsg, >::default() .withdraw_address .may_load(storage)?; @@ -536,7 +448,7 @@ fn _transfer_nft( where TMetadataExtension: Serialize + DeserializeOwned + Clone, { - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let mut token = config.nft_info.load(deps.storage, token_id)?; // ensure we have permissions check_can_send(deps.as_ref(), env, info, &token)?; @@ -561,7 +473,7 @@ fn _update_approvals( where TMetadataExtension: Serialize + DeserializeOwned + Clone, { - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let mut token = config.nft_info.load(deps.storage, token_id)?; // ensure we have permissions check_can_approve(deps.as_ref(), env, info, &token)?; @@ -604,7 +516,7 @@ where return Ok(()); } // operator can approve - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let op = config .operators .may_load(deps.storage, (&token.owner, &info.sender))?; @@ -642,7 +554,7 @@ pub fn check_can_send( } // operator can send - let config = Cw721Config::::default(); + let config = Cw721Config::::default(); let op = config .operators // has token owner approved/gave grant to sender for full control over owner's NFTs? @@ -676,24 +588,6 @@ pub fn migrate_version( Ok(response) } -pub fn migrate_creator( - storage: &mut dyn Storage, - api: &dyn Api, - _env: &Env, - msg: &Cw721MigrateMsg, - response: Response, -) -> StdResult { - match msg { - Cw721MigrateMsg::WithUpdate { creator, .. } => { - if let Some(creator) = creator { - CREATOR.initialize_owner(storage, api, Some(creator.as_str()))?; - return Ok(response.add_attribute("creator", creator)); - } - } - } - Ok(response) -} - pub fn migrate_minter( storage: &mut dyn Storage, api: &dyn Api, @@ -733,7 +627,9 @@ pub fn migrate_legacy_minter_and_creator( return Ok(response); } // in v0.17/18 cw_ownable::OWNERSHIP was used for minter, now it is used for creator - let ownership_previously_used_as_minter = CREATOR.item.may_load(storage)?; + let ownership_previously_used_as_minter = OwnershipStore::new("collection_minter") + .item + .may_load(storage)?; let creator_and_minter = match ownership_previously_used_as_minter { // v0.18 migration Some(ownership) => { @@ -748,7 +644,6 @@ pub fn migrate_legacy_minter_and_creator( let legacy_minter_store: Item = Item::new("minter"); let legacy_minter = legacy_minter_store.load(storage)?; MINTER.initialize_owner(storage, api, Some(legacy_minter.as_str()))?; - CREATOR.initialize_owner(storage, api, Some(legacy_minter.as_str()))?; Some(legacy_minter.to_string()) } }; @@ -758,16 +653,11 @@ pub fn migrate_legacy_minter_and_creator( /// Migrates only in case collection_info is not present pub fn migrate_legacy_collection_info( storage: &mut dyn Storage, - env: &Env, + _env: &Env, _msg: &Cw721MigrateMsg, response: Response, ) -> Result { - let contract = Cw721Config::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Config::::default(); match contract.collection_info.may_load(storage)? { Some(_) => Ok(response), None => { @@ -778,8 +668,6 @@ pub fn migrate_legacy_collection_info( let collection_info = CollectionInfo { name: legacy_collection_info.name.clone(), symbol: legacy_collection_info.symbol.clone(), - extension: None, - updated_at: env.block.time, }; contract.collection_info.save(storage, &collection_info)?; Ok(response diff --git a/packages/cw721/src/helpers.rs b/packages/cw721/src/helpers.rs index 259016ab0..ad1424dda 100644 --- a/packages/cw721/src/helpers.rs +++ b/packages/cw721/src/helpers.rs @@ -9,30 +9,23 @@ use crate::state::CollectionInfo; use crate::Approval; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - to_json_binary, Addr, CosmosMsg, CustomMsg, Empty, QuerierWrapper, StdResult, WasmMsg, - WasmQuery, + to_json_binary, Addr, CosmosMsg, CustomMsg, QuerierWrapper, StdResult, WasmMsg, WasmQuery, }; use serde::de::DeserializeOwned; use serde::Serialize; #[cw_serde] -pub struct Cw721Contract< - TMetadataExtension, - TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension, ->( +pub struct Cw721Contract( pub Addr, pub PhantomData, pub PhantomData, - pub PhantomData, ); #[allow(dead_code)] -impl - Cw721Contract +impl + Cw721Contract where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { pub fn addr(&self) -> Addr { self.0.clone() @@ -40,7 +33,7 @@ where pub fn call( &self, - msg: Cw721ExecuteMsg, + msg: Cw721ExecuteMsg, ) -> StdResult { let msg = to_json_binary(&msg)?; Ok(WasmMsg::Execute { @@ -54,7 +47,7 @@ where pub fn query( &self, querier: &QuerierWrapper, - req: Cw721QueryMsg, + req: Cw721QueryMsg, ) -> StdResult { let query = WasmQuery::Smart { contract_addr: self.addr().into(), @@ -134,11 +127,8 @@ where } /// With metadata extension - pub fn collection_info( - &self, - querier: &QuerierWrapper, - ) -> StdResult> { - let req = Cw721QueryMsg::GetCollectionInfo {}; + pub fn collection_info(&self, querier: &QuerierWrapper) -> StdResult { + let req = Cw721QueryMsg::ContractInfo {}; self.query(querier, req) } @@ -195,11 +185,6 @@ where self.query(querier, req) } - /// returns true if the contract supports the metadata extension - pub fn has_metadata(&self, querier: &QuerierWrapper) -> bool { - self.collection_info::(querier).is_ok() - } - /// returns true if the contract supports the enumerable extension pub fn has_enumerable(&self, querier: &QuerierWrapper) -> bool { self.tokens(querier, self.addr(), None, Some(1)).is_ok() diff --git a/packages/cw721/src/lib.rs b/packages/cw721/src/lib.rs index 0c0ff16e5..dc1f7a396 100644 --- a/packages/cw721/src/lib.rs +++ b/packages/cw721/src/lib.rs @@ -7,7 +7,7 @@ pub mod receiver; pub mod state; pub use cw_utils::Expiration; -pub use state::{Approval, CollectionInfoExtension, RoyaltyInfo}; +pub use state::Approval; #[cfg(test)] pub mod testing; diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index 809966f65..3968f2d38 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -6,19 +6,10 @@ use cw_utils::Expiration; use crate::state::CollectionInfo; use crate::Approval; -use cosmwasm_std::Empty; - #[cw_serde] -pub enum Cw721ExecuteMsg { - #[deprecated(since = "0.19.0", note = "Please use UpdateMinterOwnership instead")] +pub enum Cw721ExecuteMsg { UpdateOwnership(Action), - UpdateMinterOwnership(Action), - UpdateCreatorOwnership(Action), - /// Update the collection, can only called by the contract creator - UpdateCollectionInfo { - collection_info: CollectionInfoMsg, - }, /// Transfer is a base message to move a token to another account without triggering actions TransferNft { recipient: String, @@ -92,28 +83,23 @@ pub enum Cw721ExecuteMsg { +pub struct Cw721InstantiateMsg { /// Name of the NFT contract pub name: String, /// Symbol of the NFT contract pub symbol: String, - pub collection_info_extension: TCollectionInfoExtension, - /// The minter is the only one who can create new NFTs. /// This is designed for a base NFT that is controlled by an external program /// or contract. You will likely replace this with custom logic in custom NFTs pub minter: Option, - /// The creator is the only who can update collection info. - pub creator: Option, - pub withdraw_address: Option, } #[cw_serde] #[derive(QueryResponses)] -pub enum Cw721QueryMsg { +pub enum Cw721QueryMsg { /// Return the owner of the given token, error if token does not exist #[returns(OwnerOfResponse)] OwnerOf { @@ -154,25 +140,12 @@ pub enum Cw721QueryMsg { #[returns(NumTokensResponse)] NumTokens {}, - #[deprecated(since = "0.19.0", note = "Please use GetCollectionInfo instead")] - #[returns(CollectionInfo)] + #[returns(CollectionInfo)] ContractInfo {}, - /// With MetaData Extension. - /// Returns top-level metadata about the contract - #[returns(CollectionInfo)] - GetCollectionInfo {}, - - #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] #[returns(Ownership)] Ownership {}, - #[returns(Ownership)] - GetMinterOwnership {}, - - #[returns(Ownership)] - GetCreatorOwnership {}, - /// With MetaData Extension. /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* /// but directly from the contract @@ -205,7 +178,6 @@ pub enum Cw721QueryMsg { }, /// Return the minter - #[deprecated(since = "0.19.0", note = "Please use GetMinterOwnership instead")] #[returns(MinterResponse)] Minter {}, @@ -218,10 +190,6 @@ pub enum Cw721QueryMsg { /// Do not use - dummy extension query, needed for inferring type parameter during compile #[returns(())] Extension { msg: TMetadataExtension }, - - /// Do not use - dummy collection info extension query, needed for inferring type parameter during compile - #[returns(())] - GetCollectionInfoExtension { msg: TCollectionInfoExtension }, } #[cw_serde] @@ -233,10 +201,9 @@ pub enum Cw721MigrateMsg { } #[cw_serde] -pub struct CollectionInfoMsg { +pub struct CollectionInfoMsg { pub name: String, pub symbol: String, - pub extension: TCollectionInfoExtension, } #[cw_serde] diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index 204b4655d..5bdf2ed69 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -13,7 +13,7 @@ use crate::{ NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }, - state::{Approval, CollectionInfo, Cw721Config, NftInfo, CREATOR, MINTER}, + state::{Approval, CollectionInfo, Cw721Config, NftInfo, MINTER}, }; pub const DEFAULT_LIMIT: u32 = 10; @@ -22,28 +22,20 @@ pub const MAX_LIMIT: u32 = 1000; pub trait Cw721Query< // Metadata defined in NftInfo. TMetadataExtension, - // Extension defined in CollectionInfo. - TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn query( &self, deps: Deps, env: Env, - msg: Cw721QueryMsg, + msg: Cw721QueryMsg, ) -> StdResult { match msg { - #[allow(deprecated)] Cw721QueryMsg::Minter {} => to_json_binary(&self.query_minter(deps.storage)?), - #[allow(deprecated)] Cw721QueryMsg::ContractInfo {} => { to_json_binary(&self.query_collection_info(deps, env)?) } - Cw721QueryMsg::GetCollectionInfo {} => { - to_json_binary(&self.query_collection_info(deps, env)?) - } Cw721QueryMsg::NftInfo { token_id } => { to_json_binary(&self.query_nft_info(deps, env, token_id)?) } @@ -118,29 +110,18 @@ pub trait Cw721Query< token_id, include_expired.unwrap_or(false), )?), - #[allow(deprecated)] Cw721QueryMsg::Ownership {} => { to_json_binary(&self.query_minter_ownership(deps.storage)?) } - Cw721QueryMsg::GetMinterOwnership {} => { - to_json_binary(&self.query_minter_ownership(deps.storage)?) - } - Cw721QueryMsg::GetCreatorOwnership {} => { - to_json_binary(&self.query_creator_ownership(deps.storage)?) - } Cw721QueryMsg::Extension { msg } => { to_json_binary(&self.query_extension(deps, env, msg)?) } - Cw721QueryMsg::GetCollectionInfoExtension { msg } => { - to_json_binary(&self.query_collection_info_extension(deps, env, msg)?) - } Cw721QueryMsg::GetWithdrawAddress {} => { to_json_binary(&self.query_withdraw_address(deps)?) } } } - #[deprecated(since = "0.19.0", note = "Please use minter_ownership instead")] fn query_minter(&self, storage: &dyn Storage) -> StdResult { let minter = MINTER .get_ownership(storage)? @@ -154,24 +135,15 @@ pub trait Cw721Query< MINTER.get_ownership(storage) } - fn query_creator_ownership(&self, storage: &dyn Storage) -> StdResult> { - CREATOR.get_ownership(storage) - } - - fn query_collection_info( - &self, - deps: Deps, - _env: Env, - ) -> StdResult> { - Cw721Config::::default() + fn query_collection_info(&self, deps: Deps, _env: Env) -> StdResult { + Cw721Config::::default() .collection_info .load(deps.storage) } fn query_num_tokens(&self, deps: Deps, _env: Env) -> StdResult { let count = - Cw721Config::::default() - .token_count(deps.storage)?; + Cw721Config::::default().token_count(deps.storage)?; Ok(NumTokensResponse { count }) } @@ -181,10 +153,9 @@ pub trait Cw721Query< _env: Env, token_id: String, ) -> StdResult> { - let info = - Cw721Config::::default() - .nft_info - .load(deps.storage, &token_id)?; + let info = Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; Ok(NftInfoResponse { token_uri: info.token_uri, extension: info.extension, @@ -198,10 +169,9 @@ pub trait Cw721Query< token_id: String, include_expired_approval: bool, ) -> StdResult { - let nft_info = - Cw721Config::::default() - .nft_info - .load(deps.storage, &token_id)?; + let nft_info = Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; Ok(OwnerOfResponse { owner: nft_info.owner.to_string(), approvals: humanize_approvals(&env.block, &nft_info, include_expired_approval), @@ -220,10 +190,9 @@ pub trait Cw721Query< let owner_addr = deps.api.addr_validate(&owner)?; let operator_addr = deps.api.addr_validate(&operator)?; - let info = - Cw721Config::::default() - .operators - .may_load(deps.storage, (&owner_addr, &operator_addr))?; + let info = Cw721Config::::default() + .operators + .may_load(deps.storage, (&owner_addr, &operator_addr))?; if let Some(expires) = info { if !include_expired_approval && expires.is_expired(&env.block) { @@ -256,19 +225,18 @@ pub trait Cw721Query< let start = start_addr.as_ref().map(Bound::exclusive); let owner_addr = deps.api.addr_validate(&owner)?; - let res: StdResult> = - Cw721Config::::default() - .operators - .prefix(&owner_addr) - .range(deps.storage, start, None, Order::Ascending) - .filter(|r| { - include_expired_approval - || r.is_err() - || !r.as_ref().unwrap().1.is_expired(&env.block) - }) - .take(limit) - .map(parse_approval) - .collect(); + let res: StdResult> = Cw721Config::::default() + .operators + .prefix(&owner_addr) + .range(deps.storage, start, None, Order::Ascending) + .filter(|r| { + include_expired_approval + || r.is_err() + || !r.as_ref().unwrap().1.is_expired(&env.block) + }) + .take(limit) + .map(parse_approval) + .collect(); Ok(OperatorsResponse { operators: res? }) } @@ -280,10 +248,9 @@ pub trait Cw721Query< spender: String, include_expired_approval: bool, ) -> StdResult { - let token = - Cw721Config::::default() - .nft_info - .load(deps.storage, &token_id)?; + let token = Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; // token owner has absolute approval if token.owner == spender { @@ -322,10 +289,9 @@ pub trait Cw721Query< token_id: String, include_expired_approval: bool, ) -> StdResult { - let token = - Cw721Config::::default() - .nft_info - .load(deps.storage, &token_id)?; + let token = Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; let approvals: Vec<_> = token .approvals .into_iter() @@ -351,15 +317,14 @@ pub trait Cw721Query< let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); let owner_addr = deps.api.addr_validate(&owner)?; - let tokens: Vec = - Cw721Config::::default() - .nft_info - .idx - .owner - .prefix(owner_addr) - .keys(deps.storage, start, None, Order::Ascending) - .take(limit) - .collect::>>()?; + let tokens: Vec = Cw721Config::::default() + .nft_info + .idx + .owner + .prefix(owner_addr) + .keys(deps.storage, start, None, Order::Ascending) + .take(limit) + .collect::>>()?; Ok(TokensResponse { tokens }) } @@ -375,7 +340,7 @@ pub trait Cw721Query< let start = start_after.map(|s| Bound::ExclusiveRaw(s.into())); let tokens: StdResult> = - Cw721Config::::default() + Cw721Config::::default() .nft_info .range(deps.storage, start, None, Order::Ascending) .take(limit) @@ -392,10 +357,9 @@ pub trait Cw721Query< token_id: String, include_expired_approval: bool, ) -> StdResult> { - let nft_info = - Cw721Config::::default() - .nft_info - .load(deps.storage, &token_id)?; + let nft_info = Cw721Config::::default() + .nft_info + .load(deps.storage, &token_id)?; Ok(AllNftInfoResponse { access: OwnerOfResponse { owner: nft_info.owner.to_string(), @@ -418,18 +382,8 @@ pub trait Cw721Query< Ok(Binary::default()) } - /// No-op returning empty Binary - fn query_collection_info_extension( - &self, - _deps: Deps, - _env: Env, - _msg: TCollectionInfoExtension, - ) -> StdResult { - Ok(Binary::default()) - } - fn query_withdraw_address(&self, deps: Deps) -> StdResult> { - Cw721Config::::default() + Cw721Config::::default() .withdraw_address .may_load(deps.storage) } diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index 4c062f732..9dc4ac097 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -1,23 +1,17 @@ use std::marker::PhantomData; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, BlockInfo, CustomMsg, Decimal, StdResult, Storage, Timestamp}; +use cosmwasm_std::{Addr, BlockInfo, CustomMsg, StdResult, Storage}; use cw_ownable::{OwnershipStore, OWNERSHIP_KEY}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; use cw_utils::Expiration; use serde::de::DeserializeOwned; use serde::Serialize; -/// Creator owns this contract and can update collection info! -/// !!! Important note here: !!! -/// - creator is stored using using cw-ownable's OWNERSHIP singleton, so it is not stored here -/// - in release v0.18.0 it was used for minter (which is confusing), but now it is used for creator -pub const CREATOR: OwnershipStore = OwnershipStore::new(OWNERSHIP_KEY); /// - minter is stored in the contract storage using cw_ownable::OwnershipStore (same as for OWNERSHIP but with different key) -pub const MINTER: OwnershipStore = OwnershipStore::new("collection_minter"); +pub const MINTER: OwnershipStore = OwnershipStore::new(OWNERSHIP_KEY); /// Default CollectionInfoExtension with RoyaltyInfo -pub type DefaultOptionCollectionInfoExtension = Option>; pub type DefaultOptionMetadataExtension = Option; pub struct Cw721Config< @@ -28,15 +22,12 @@ pub struct Cw721Config< TCustomResponseMessage, // Message passed for updating metadata. TMetadataExtensionMsg, - // Extension defined in CollectionInfo. - TCollectionInfoExtension, > where TMetadataExtension: Serialize + DeserializeOwned + Clone, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { /// Note: replaces deprecated/legacy key "nft_info"! - pub collection_info: Item<'a, CollectionInfo>, + pub collection_info: Item<'a, CollectionInfo>, pub token_count: Item<'a, u64>, /// Stored as (granter, operator) giving operator full control over granter's account. /// NOTE: granter is the owner, so operator has only control for NFTs owned by granter! @@ -49,23 +40,11 @@ pub struct Cw721Config< pub(crate) _custom_execute: PhantomData, } -impl< - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > Default - for Cw721Config< - 'static, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > +impl Default + for Cw721Config<'static, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { Self::new( @@ -79,24 +58,11 @@ where } } -impl< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > - Cw721Config< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> + Cw721Config<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn new( collection_info_key: &'a str, @@ -192,27 +158,9 @@ where } #[cw_serde] -pub struct CollectionInfo { +pub struct CollectionInfo { pub name: String, pub symbol: String, - pub extension: TCollectionInfoExtension, - pub updated_at: Timestamp, -} - -#[cw_serde] -pub struct CollectionInfoExtension { - pub description: String, - pub image: String, - pub external_link: Option, - pub explicit_content: Option, - pub start_trading_time: Option, - pub royalty_info: Option, -} - -#[cw_serde] -pub struct RoyaltyInfo { - pub payment_address: Addr, - pub share: Decimal, } // see: https://docs.opensea.io/docs/metadata-standards diff --git a/packages/cw721/src/testing/contract.rs b/packages/cw721/src/testing/contract.rs index 4e8c571e5..4a1ab0c33 100644 --- a/packages/cw721/src/testing/contract.rs +++ b/packages/cw721/src/testing/contract.rs @@ -6,43 +6,19 @@ use crate::execute::Cw721Execute; use crate::query::Cw721Query; use crate::state::Cw721Config; -pub struct Cw721Contract< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, -> where +pub struct Cw721Contract<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> +where TMetadataExtension: Serialize + DeserializeOwned + Clone, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { - pub config: Cw721Config< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - >, + pub config: Cw721Config<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg>, } -impl< - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > Default - for Cw721Contract< - 'static, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > +impl Default + for Cw721Contract<'static, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { fn default() -> Self { Self { @@ -51,52 +27,22 @@ where } } -impl< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > - Cw721Execute< - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > - for Cw721Contract< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> + Cw721Execute + for Cw721Contract<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } -impl< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > Cw721Query - for Cw721Contract< - 'a, - TMetadataExtension, - TCustomResponseMessage, - TMetadataExtensionMsg, - TCollectionInfoExtension, - > +impl<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> + Cw721Query + for Cw721Contract<'a, TMetadataExtension, TCustomResponseMessage, TMetadataExtensionMsg> where TMetadataExtension: Serialize + DeserializeOwned + Clone, TCustomResponseMessage: CustomMsg, TMetadataExtensionMsg: CustomMsg, - TCollectionInfoExtension: Serialize + DeserializeOwned + Clone, { } diff --git a/packages/cw721/src/testing/contract_tests.rs b/packages/cw721/src/testing/contract_tests.rs index 603d64832..e7c81d461 100644 --- a/packages/cw721/src/testing/contract_tests.rs +++ b/packages/cw721/src/testing/contract_tests.rs @@ -1,23 +1,19 @@ #![cfg(test)] + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{ - from_json, to_json_binary, Addr, Coin, CosmosMsg, DepsMut, Empty, Response, StdError, - Timestamp, WasmMsg, + from_json, to_json_binary, Addr, Coin, CosmosMsg, DepsMut, Empty, Response, StdError, WasmMsg, }; use crate::error::Cw721ContractError; use crate::msg::{ ApprovalResponse, NftInfoResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, }; -use crate::msg::{CollectionInfoMsg, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721QueryMsg}; +use crate::msg::{Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721QueryMsg}; use crate::receiver::Cw721ReceiveMsg; -use crate::state::{ - CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension, CREATOR, - MINTER, -}; +use crate::state::{CollectionInfo, DefaultOptionMetadataExtension, MINTER}; use crate::{execute::Cw721Execute, query::Cw721Query, Approval, Expiration}; -use crate::{CollectionInfoExtension, RoyaltyInfo}; use cw_ownable::{Action, Ownership, OwnershipError}; use super::contract::Cw721Contract; @@ -29,14 +25,12 @@ const SYMBOL: &str = "MGK"; fn setup_contract( deps: DepsMut<'_>, -) -> Cw721Contract<'static, DefaultOptionMetadataExtension, Empty, Empty, Empty> { +) -> Cw721Contract<'static, DefaultOptionMetadataExtension, Empty, Empty> { let contract = Cw721Contract::default(); let msg = Cw721InstantiateMsg { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - collection_info_extension: Empty {}, minter: Some(String::from(MINTER_ADDR)), - creator: Some(String::from(CREATOR_ADDR)), withdraw_address: None, }; let info = mock_info("creator", &[]); @@ -57,14 +51,12 @@ fn setup_contract( #[test] fn proper_instantiation() { let mut deps = mock_dependencies(); - let contract = Cw721Contract::::default(); + let contract = Cw721Contract::::default(); let msg = Cw721InstantiateMsg { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - collection_info_extension: Empty {}, minter: Some(String::from(MINTER_ADDR)), - creator: Some(String::from(CREATOR_ADDR)), withdraw_address: Some(String::from(CREATOR_ADDR)), }; let info = mock_info("creator", &[]); @@ -86,8 +78,6 @@ fn proper_instantiation() { // it worked, let's query the state let minter_ownership = MINTER.get_ownership(deps.as_ref().storage).unwrap(); assert_eq!(Some(Addr::unchecked(MINTER_ADDR)), minter_ownership.owner); - let creator_ownership = CREATOR.get_ownership(deps.as_ref().storage).unwrap(); - assert_eq!(Some(Addr::unchecked(CREATOR_ADDR)), creator_ownership.owner); let collection_info = contract .query_collection_info(deps.as_ref(), env.clone()) .unwrap(); @@ -96,8 +86,6 @@ fn proper_instantiation() { CollectionInfo { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - extension: Empty {}, - updated_at: env.block.time } ); @@ -123,30 +111,12 @@ fn proper_instantiation() { #[test] fn proper_instantiation_with_collection_info() { let mut deps = mock_dependencies(); - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); - - let collection_info_extension = Some(CollectionInfoExtension { - description: "description".to_string(), - image: "image".to_string(), - explicit_content: Some(true), - external_link: Some("external_link".to_string()), - start_trading_time: Some(Timestamp::from_seconds(0)), - royalty_info: Some(RoyaltyInfo { - payment_address: Addr::unchecked("payment_address"), - share: "0.1".parse().unwrap(), - }), - }); - let msg = Cw721InstantiateMsg:: { + let contract = Cw721Contract::::default(); + + let msg = Cw721InstantiateMsg { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - collection_info_extension: collection_info_extension.clone(), minter: Some(String::from(MINTER_ADDR)), - creator: Some(String::from(CREATOR_ADDR)), withdraw_address: Some(String::from(CREATOR_ADDR)), }; let collection_info = mock_info("creator", &[]); @@ -168,8 +138,6 @@ fn proper_instantiation_with_collection_info() { // it worked, let's query the state let minter_ownership = MINTER.get_ownership(deps.as_ref().storage).unwrap(); assert_eq!(Some(Addr::unchecked(MINTER_ADDR)), minter_ownership.owner); - let creator_ownership = CREATOR.get_ownership(deps.as_ref().storage).unwrap(); - assert_eq!(Some(Addr::unchecked(CREATOR_ADDR)), creator_ownership.owner); let info = contract .query_collection_info(deps.as_ref(), env.clone()) .unwrap(); @@ -178,8 +146,6 @@ fn proper_instantiation_with_collection_info() { CollectionInfo { name: CONTRACT_NAME.to_string(), symbol: SYMBOL.to_string(), - extension: collection_info_extension, - updated_at: env.block.time } ); @@ -288,120 +254,6 @@ fn minting() { assert_eq!(vec![token_id], tokens.tokens); } -#[test] -fn test_update_collection_info() { - let mut deps = mock_dependencies(); - let contract = setup_contract(deps.as_mut()); - - let update_collection_info_msg = Cw721ExecuteMsg::UpdateCollectionInfo { - collection_info: CollectionInfoMsg { - name: "new name".to_string(), - symbol: "NEW".to_string(), - extension: Empty {}, - }, - }; - - // Creator can update collection info - let creator_info = mock_info(CREATOR_ADDR, &[]); - let _ = contract - .execute( - deps.as_mut(), - mock_env(), - creator_info.clone(), - update_collection_info_msg, - ) - .unwrap(); - - // Update the owner to "random". The new owner should be able to - // mint new tokens, the old one should not. - contract - .execute( - deps.as_mut(), - mock_env(), - creator_info.clone(), - Cw721ExecuteMsg::UpdateCreatorOwnership(Action::TransferOwnership { - new_owner: "random".to_string(), - expiry: None, - }), - ) - .unwrap(); - - // Creator does not change until ownership transfer completes. - // Pending ownership transfer should be discoverable via query. - let ownership: Ownership = from_json( - contract - .query( - deps.as_ref(), - mock_env(), - Cw721QueryMsg::GetCreatorOwnership {}, - ) - .unwrap(), - ) - .unwrap(); - - assert_eq!( - ownership, - Ownership:: { - owner: Some(Addr::unchecked(CREATOR_ADDR)), - pending_owner: Some(Addr::unchecked("random")), - pending_expiry: None, - } - ); - - // Accept the ownership transfer. - let random_info = mock_info("random", &[]); - contract - .execute( - deps.as_mut(), - mock_env(), - random_info.clone(), - Cw721ExecuteMsg::UpdateCreatorOwnership(Action::AcceptOwnership), - ) - .unwrap(); - - // Creator changes after ownership transfer is accepted. - let creator_ownership: Ownership = from_json( - contract - .query( - deps.as_ref(), - mock_env(), - Cw721QueryMsg::GetCreatorOwnership {}, - ) - .unwrap(), - ) - .unwrap(); - assert_eq!(creator_ownership.owner, Some(random_info.sender.clone())); - - let update_collection_info_msg = Cw721ExecuteMsg::UpdateCollectionInfo { - collection_info: CollectionInfoMsg { - name: "new name".to_string(), - symbol: "NEW".to_string(), - extension: Empty {}, - }, - }; - - // Old owner can not update. - let err: Cw721ContractError = contract - .execute( - deps.as_mut(), - mock_env(), - creator_info, - update_collection_info_msg.clone(), - ) - .unwrap_err(); - assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); - - // New owner can update. - let _ = contract - .execute( - deps.as_mut(), - mock_env(), - random_info, - update_collection_info_msg, - ) - .unwrap(); -} - #[test] fn test_update_minter() { let mut deps = mock_dependencies(); @@ -430,7 +282,7 @@ fn test_update_minter() { deps.as_mut(), mock_env(), minter_info.clone(), - Cw721ExecuteMsg::UpdateMinterOwnership(Action::TransferOwnership { + Cw721ExecuteMsg::UpdateOwnership(Action::TransferOwnership { new_owner: "random".to_string(), expiry: None, }), @@ -441,11 +293,7 @@ fn test_update_minter() { // Pending ownership transfer should be discoverable via query. let ownership: Ownership = from_json( contract - .query( - deps.as_ref(), - mock_env(), - Cw721QueryMsg::GetMinterOwnership {}, - ) + .query(deps.as_ref(), mock_env(), Cw721QueryMsg::Ownership {}) .unwrap(), ) .unwrap(); @@ -466,18 +314,14 @@ fn test_update_minter() { deps.as_mut(), mock_env(), random_info.clone(), - Cw721ExecuteMsg::UpdateMinterOwnership(Action::AcceptOwnership), + Cw721ExecuteMsg::UpdateOwnership(Action::AcceptOwnership), ) .unwrap(); // Minter changes after ownership transfer is accepted. let minter_ownership: Ownership = from_json( contract - .query( - deps.as_ref(), - mock_env(), - Cw721QueryMsg::GetMinterOwnership {}, - ) + .query(deps.as_ref(), mock_env(), Cw721QueryMsg::Ownership {}) .unwrap(), ) .unwrap(); @@ -1103,21 +947,17 @@ fn test_set_withdraw_address() { let mut deps = mock_dependencies(); let contract = setup_contract(deps.as_mut()); - // other than creator cant set + // other than minter cant set let err = contract - .set_withdraw_address( - deps.as_mut(), - &Addr::unchecked(MINTER_ADDR), - "foo".to_string(), - ) + .set_withdraw_address(deps.as_mut(), &Addr::unchecked("other"), "foo".to_string()) .unwrap_err(); assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); - // creator can set + // minter can set contract .set_withdraw_address( deps.as_mut(), - &Addr::unchecked(CREATOR_ADDR), + &Addr::unchecked(MINTER_ADDR), "foo".to_string(), ) .unwrap(); @@ -1137,13 +977,13 @@ fn test_remove_withdraw_address() { // other than creator cant remove let err = contract - .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(MINTER_ADDR)) + .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked("other")) .unwrap_err(); assert_eq!(err, Cw721ContractError::Ownership(OwnershipError::NotOwner)); // no withdraw address set yet let err = contract - .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(CREATOR_ADDR)) + .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(MINTER_ADDR)) .unwrap_err(); assert_eq!(err, Cw721ContractError::NoWithdrawAddress {}); @@ -1151,12 +991,12 @@ fn test_remove_withdraw_address() { contract .set_withdraw_address( deps.as_mut(), - &Addr::unchecked(CREATOR_ADDR), + &Addr::unchecked(MINTER_ADDR), "foo".to_string(), ) .unwrap(); contract - .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(CREATOR_ADDR)) + .remove_withdraw_address(deps.as_mut().storage, &Addr::unchecked(MINTER_ADDR)) .unwrap(); assert!(!contract .config @@ -1167,7 +1007,7 @@ fn test_remove_withdraw_address() { contract .set_withdraw_address( deps.as_mut(), - &Addr::unchecked(CREATOR_ADDR), + &Addr::unchecked(MINTER_ADDR), "foo".to_string(), ) .unwrap(); @@ -1190,11 +1030,11 @@ fn test_withdraw_funds() { .unwrap_err(); assert_eq!(err, Cw721ContractError::NoWithdrawAddress {}); - // set and withdraw by non-creator + // set and withdraw by non-owner contract .set_withdraw_address( deps.as_mut(), - &Addr::unchecked(CREATOR_ADDR), + &Addr::unchecked(MINTER_ADDR), "foo".to_string(), ) .unwrap(); diff --git a/packages/cw721/src/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index 89f2211f9..4bbd08228 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -6,7 +6,7 @@ use crate::{ OwnerOfResponse, }, query::Cw721Query, - state::{DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension}, + state::DefaultOptionMetadataExtension, }; use cosmwasm_std::{ to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, QuerierWrapper, Response, @@ -27,14 +27,9 @@ pub fn instantiate( deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721InstantiateMsg, + msg: Cw721InstantiateMsg, ) -> Result { - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Contract::::default(); contract.instantiate(deps, env, info, msg, "contract_name", "contract_version") } @@ -42,32 +37,18 @@ pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721ExecuteMsg< - DefaultOptionMetadataExtension, - Empty, - DefaultOptionCollectionInfoExtension, - >, + msg: Cw721ExecuteMsg, ) -> Result { - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Contract::::default(); contract.execute(deps, env, info, msg) } pub fn query( deps: Deps, env: Env, - msg: Cw721QueryMsg, + msg: Cw721QueryMsg, ) -> StdResult { - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Contract::::default(); contract.query(deps, env, msg) } @@ -76,12 +57,7 @@ pub fn migrate( env: Env, msg: Cw721MigrateMsg, ) -> Result { - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Contract::::default(); contract.migrate(deps, env, msg, "contract_name", "contract_version") } @@ -124,7 +100,7 @@ fn query_owner(querier: QuerierWrapper, cw721: &Addr, token_id: String) -> Addr let resp: OwnerOfResponse = querier .query_wasm_smart( cw721, - &Cw721QueryMsg::::OwnerOf { + &Cw721QueryMsg::::OwnerOf { token_id, include_expired: None, }, @@ -137,7 +113,7 @@ fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: St app.execute_contract( sender.clone(), cw721.clone(), - &Cw721ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: token_id.clone(), owner: sender.to_string(), token_uri: None, @@ -153,7 +129,7 @@ fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: St app.execute_contract( sender, cw721.clone(), - &Cw721ExecuteMsg::::TransferNft { + &Cw721ExecuteMsg::::TransferNft { recipient: "burner".to_string(), token_id: token_id.clone(), }, @@ -167,7 +143,7 @@ fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: St app.execute_contract( Addr::unchecked("burner"), cw721, - &Cw721ExecuteMsg::::Burn { token_id }, + &Cw721ExecuteMsg::::Burn { token_id }, &[], ) .unwrap(); @@ -184,12 +160,10 @@ fn test_operator() { .instantiate_contract( code_id, other.clone(), - &Cw721InstantiateMsg:: { + &Cw721InstantiateMsg { name: "collection".to_string(), symbol: "symbol".to_string(), minter: Some(MINTER_ADDR.to_string()), - creator: Some(CREATOR_ADDR.to_string()), - collection_info_extension: None, withdraw_address: None, }, &[], @@ -203,7 +177,7 @@ fn test_operator() { app.execute_contract( minter, cw721.clone(), - &Cw721ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: nft_owner.to_string(), token_uri: None, @@ -218,7 +192,7 @@ fn test_operator() { app.execute_contract( nft_owner.clone(), cw721.clone(), - &Cw721ExecuteMsg::::ApproveAll { + &Cw721ExecuteMsg::::ApproveAll { operator: other.to_string(), expires: Some(Expiration::Never {}), }, @@ -230,7 +204,7 @@ fn test_operator() { app.execute_contract( other.clone(), cw721.clone(), - &Cw721ExecuteMsg::::TransferNft { + &Cw721ExecuteMsg::::TransferNft { recipient: other.to_string(), token_id: "1".to_string(), }, @@ -242,7 +216,7 @@ fn test_operator() { .wrap() .query_wasm_smart( &cw721, - &Cw721QueryMsg::::OwnerOf { + &Cw721QueryMsg::::OwnerOf { token_id: "1".to_string(), include_expired: None, }, @@ -254,7 +228,7 @@ fn test_operator() { .execute_contract( nft_owner.clone(), cw721.clone(), - &Cw721ExecuteMsg::::TransferNft { + &Cw721ExecuteMsg::::TransferNft { recipient: other.to_string(), token_id: "1".to_string(), }, @@ -269,7 +243,7 @@ fn test_operator() { app.execute_contract( other.clone(), cw721.clone(), - &Cw721ExecuteMsg::::TransferNft { + &Cw721ExecuteMsg::::TransferNft { recipient: nft_owner.to_string(), token_id: "1".to_string(), }, @@ -281,7 +255,7 @@ fn test_operator() { .wrap() .query_wasm_smart( &cw721, - &Cw721QueryMsg::::OwnerOf { + &Cw721QueryMsg::::OwnerOf { token_id: "1".to_string(), include_expired: None, }, @@ -293,7 +267,7 @@ fn test_operator() { app.execute_contract( other.clone(), cw721.clone(), - &Cw721ExecuteMsg::::TransferNft { + &Cw721ExecuteMsg::::TransferNft { recipient: other.to_string(), token_id: "1".to_string(), }, @@ -305,7 +279,7 @@ fn test_operator() { .wrap() .query_wasm_smart( &cw721, - &Cw721QueryMsg::::OwnerOf { + &Cw721QueryMsg::::OwnerOf { token_id: "1".to_string(), include_expired: None, }, @@ -318,7 +292,7 @@ fn test_operator() { app.execute_contract( other.clone(), cw721.clone(), - &Cw721ExecuteMsg::::TransferNft { + &Cw721ExecuteMsg::::TransferNft { recipient: nft_owner.to_string(), token_id: "1".to_string(), }, @@ -330,7 +304,7 @@ fn test_operator() { app.execute_contract( nft_owner, cw721.clone(), - &Cw721ExecuteMsg::::RevokeAll { + &Cw721ExecuteMsg::::RevokeAll { operator: other.to_string(), }, &[], @@ -342,7 +316,7 @@ fn test_operator() { .execute_contract( other.clone(), cw721, - &Cw721ExecuteMsg::::TransferNft { + &Cw721ExecuteMsg::::TransferNft { recipient: other.to_string(), token_id: "1".to_string(), }, @@ -413,7 +387,7 @@ fn test_migration_legacy_to_latest() { .execute_contract( other.clone(), cw721.clone(), - &Cw721ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: other.to_string(), token_uri: None, @@ -435,44 +409,26 @@ fn test_migration_legacy_to_latest() { ); // check new mint query response works. - #[allow(deprecated)] let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); // check that the new response is backwards compatable when minter // is not None. - #[allow(deprecated)] let m: v16::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, legacy_creator_and_minter.to_string()); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetMinterOwnership {}, - ) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Ownership {}) .unwrap(); - assert_eq!( - minter_ownership.owner, - Some(legacy_creator_and_minter.clone()) - ); - - // check creator ownership query works - let creator_ownership: Ownership = app - .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetCreatorOwnership {}, - ) - .unwrap(); - assert_eq!(creator_ownership.owner, Some(legacy_creator_and_minter)); + assert_eq!(minter_ownership.owner, Some(legacy_creator_and_minter)); } // case 2: migrate from v0.16 to latest by providing new creator and minter addr { @@ -528,7 +484,7 @@ fn test_migration_legacy_to_latest() { .execute_contract( legacy_creator_and_minter.clone(), cw721.clone(), - &Cw721ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: legacy_creator_and_minter.to_string(), token_uri: None, @@ -546,42 +502,26 @@ fn test_migration_legacy_to_latest() { mint_transfer_and_burn(&mut app, cw721.clone(), minter.clone(), "1".to_string()); // check new mint query response works. - #[allow(deprecated)] let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(minter.to_string())); // check that the new response is backwards compatable when minter // is not None. - #[allow(deprecated)] let m: v16::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, minter.to_string()); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetMinterOwnership {}, - ) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Ownership {}) .unwrap(); assert_eq!(minter_ownership.owner, Some(minter)); - - // check creator ownership query works - let creator = Addr::unchecked(CREATOR_ADDR); - let creator_ownership: Ownership = app - .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetCreatorOwnership {}, - ) - .unwrap(); - assert_eq!(creator_ownership.owner, Some(creator)); } // case 3: migrate from v0.17 to latest by using existing minter addr { @@ -638,7 +578,7 @@ fn test_migration_legacy_to_latest() { .execute_contract( other.clone(), cw721.clone(), - &Cw721ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: other.to_string(), token_uri: None, @@ -660,44 +600,26 @@ fn test_migration_legacy_to_latest() { ); // check new mint query response works. - #[allow(deprecated)] let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); // check that the new response is backwards compatable when minter // is not None. - #[allow(deprecated)] let m: v17::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetMinterOwnership {}, - ) - .unwrap(); - assert_eq!( - minter_ownership.owner, - Some(legacy_creator_and_minter.clone()) - ); - - // check creator ownership query works - let creator_ownership: Ownership = app - .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetCreatorOwnership {}, - ) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Ownership {}) .unwrap(); - assert_eq!(creator_ownership.owner, Some(legacy_creator_and_minter)); + assert_eq!(minter_ownership.owner, Some(legacy_creator_and_minter)); } // case 4: migrate from v0.17 to latest by providing new creator and minter addr { @@ -753,7 +675,7 @@ fn test_migration_legacy_to_latest() { .execute_contract( legacy_creator_and_minter.clone(), cw721.clone(), - &Cw721ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: legacy_creator_and_minter.to_string(), token_uri: None, @@ -771,42 +693,26 @@ fn test_migration_legacy_to_latest() { mint_transfer_and_burn(&mut app, cw721.clone(), minter.clone(), "1".to_string()); // check new mint query response works. - #[allow(deprecated)] let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(minter.to_string())); // check that the new response is backwards compatable when minter // is not None. - #[allow(deprecated)] let m: v17::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(minter.to_string())); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetMinterOwnership {}, - ) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Ownership {}) .unwrap(); assert_eq!(minter_ownership.owner, Some(minter)); - - // check creator ownership query works - let creator = Addr::unchecked(CREATOR_ADDR); - let creator_ownership: Ownership = app - .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetCreatorOwnership {}, - ) - .unwrap(); - assert_eq!(creator_ownership.owner, Some(creator)); } // case 5: migrate from v0.18 to latest by using existing minter addr { @@ -863,7 +769,7 @@ fn test_migration_legacy_to_latest() { .execute_contract( other.clone(), cw721.clone(), - &Cw721ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: other.to_string(), token_uri: None, @@ -885,44 +791,26 @@ fn test_migration_legacy_to_latest() { ); // check new mint query response works. - #[allow(deprecated)] let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); // check that the new response is backwards compatable when minter // is not None. - #[allow(deprecated)] let m: v18::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(legacy_creator_and_minter.to_string())); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetMinterOwnership {}, - ) - .unwrap(); - assert_eq!( - minter_ownership.owner, - Some(legacy_creator_and_minter.clone()) - ); - - // check creator ownership query works - let creator_ownership: Ownership = app - .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetCreatorOwnership {}, - ) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Ownership {}) .unwrap(); - assert_eq!(creator_ownership.owner, Some(legacy_creator_and_minter)); + assert_eq!(minter_ownership.owner, Some(legacy_creator_and_minter)); } // case 6: migrate from v0.18 to latest by providing new creator and minter addr { @@ -978,7 +866,7 @@ fn test_migration_legacy_to_latest() { .execute_contract( legacy_creator_and_minter.clone(), cw721.clone(), - &Cw721ExecuteMsg::::Mint { + &Cw721ExecuteMsg::::Mint { token_id: "1".to_string(), owner: legacy_creator_and_minter.to_string(), token_uri: None, @@ -996,42 +884,26 @@ fn test_migration_legacy_to_latest() { mint_transfer_and_burn(&mut app, cw721.clone(), minter.clone(), "1".to_string()); // check new mint query response works. - #[allow(deprecated)] let m: MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(minter.to_string())); // check that the new response is backwards compatable when minter // is not None. - #[allow(deprecated)] let m: v18::MinterResponse = app .wrap() - .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Minter {}) .unwrap(); assert_eq!(m.minter, Some(minter.to_string())); // check minter ownership query works let minter_ownership: Ownership = app .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetMinterOwnership {}, - ) + .query_wasm_smart(&cw721, &Cw721QueryMsg::::Ownership {}) .unwrap(); assert_eq!(minter_ownership.owner, Some(minter)); - - // check creator ownership query works - let creator = Addr::unchecked(CREATOR_ADDR); - let creator_ownership: Ownership = app - .wrap() - .query_wasm_smart( - &cw721, - &Cw721QueryMsg::::GetCreatorOwnership {}, - ) - .unwrap(); - assert_eq!(creator_ownership.owner, Some(creator)); } } @@ -1063,7 +935,7 @@ fn test_instantiate_016_msg() { // assert withdraw address is None let withdraw_addr: Option = app .wrap() - .query_wasm_smart(cw721, &Cw721QueryMsg::::GetWithdrawAddress {}) + .query_wasm_smart(cw721, &Cw721QueryMsg::::GetWithdrawAddress {}) .unwrap(); assert!(withdraw_addr.is_none()); } diff --git a/packages/cw721/src/testing/unit_tests.rs b/packages/cw721/src/testing/unit_tests.rs index 31783ef78..dcac842f0 100644 --- a/packages/cw721/src/testing/unit_tests.rs +++ b/packages/cw721/src/testing/unit_tests.rs @@ -2,10 +2,7 @@ use crate::{ execute::Cw721Execute, msg::{Cw721ExecuteMsg, Cw721InstantiateMsg}, query::{Cw721Query, MAX_LIMIT}, - state::{ - CollectionInfo, DefaultOptionCollectionInfoExtension, DefaultOptionMetadataExtension, - Metadata, CREATOR, MINTER, - }, + state::{CollectionInfo, DefaultOptionMetadataExtension, Metadata, MINTER}, }; use cosmwasm_std::{ testing::{mock_dependencies, mock_env, mock_info}, @@ -22,28 +19,21 @@ use super::*; fn proper_cw2_initialization() { let mut deps = mock_dependencies(); - Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default() - .instantiate( - deps.as_mut(), - mock_env(), - mock_info("larry", &[]), - Cw721InstantiateMsg { - name: "collection_name".into(), - symbol: "collection_symbol".into(), - collection_info_extension: None, - minter: Some("minter".into()), - creator: Some("creator".into()), - withdraw_address: None, - }, - "contract_name", - "contract_version", - ) - .unwrap(); + Cw721Contract::::default() + .instantiate( + deps.as_mut(), + mock_env(), + mock_info("larry", &[]), + Cw721InstantiateMsg { + name: "collection_name".into(), + symbol: "collection_symbol".into(), + minter: Some("minter".into()), + withdraw_address: None, + }, + "contract_name", + "contract_version", + ) + .unwrap(); let minter = MINTER .get_ownership(deps.as_ref().storage) @@ -52,13 +42,6 @@ fn proper_cw2_initialization() { .map(|a| a.into_string()); assert_eq!(minter, Some("minter".to_string())); - let creator = CREATOR - .get_ownership(deps.as_ref().storage) - .unwrap() - .owner - .map(|a| a.into_string()); - assert_eq!(creator, Some("creator".to_string())); - let version = cw2::get_contract_version(deps.as_ref().storage).unwrap(); assert_eq!( version, @@ -74,52 +57,36 @@ fn proper_owner_initialization() { let mut deps = mock_dependencies(); let info_owner = mock_info("owner", &[]); - Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default() - .instantiate( - deps.as_mut(), - mock_env(), - info_owner.clone(), - Cw721InstantiateMsg { - name: "collection_name".into(), - symbol: "collection_symbol".into(), - collection_info_extension: None, - creator: None, - minter: None, - withdraw_address: None, - }, - "contract_name", - "contract_version", - ) - .unwrap(); + Cw721Contract::::default() + .instantiate( + deps.as_mut(), + mock_env(), + info_owner.clone(), + Cw721InstantiateMsg { + name: "collection_name".into(), + symbol: "collection_symbol".into(), + minter: None, + withdraw_address: None, + }, + "contract_name", + "contract_version", + ) + .unwrap(); let minter = MINTER.item.load(deps.as_ref().storage).unwrap().owner; assert_eq!(minter, Some(info_owner.sender)); - let creator = CREATOR.item.load(deps.as_ref().storage).unwrap().owner; - assert_eq!(creator, Some(Addr::unchecked("owner"))); } #[test] fn use_metadata_extension() { let mut deps = mock_dependencies(); - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Contract::::default(); let info = mock_info(CREATOR_ADDR, &[]); let init_msg = Cw721InstantiateMsg { name: "collection_name".into(), symbol: "collection_symbol".into(), - collection_info_extension: None, minter: None, - creator: None, withdraw_address: None, }; let env = mock_env(); @@ -191,18 +158,12 @@ fn test_migrate() { // assert new data before migration: // - ownership and collection info throws NotFound Error MINTER.item.load(deps.as_ref().storage).unwrap_err(); // cw_ownable in v16 is used for minter - let contract = Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default(); + let contract = Cw721Contract::::default(); contract .query_collection_info(deps.as_ref(), env.clone()) .unwrap_err(); // - query in new minter and creator ownership store throws NotFound Error (in v16 it was stored outside cw_ownable, in dedicated "minter" store) MINTER.get_ownership(deps.as_ref().storage).unwrap_err(); - CREATOR.get_ownership(deps.as_ref().storage).unwrap_err(); // assert legacy data before migration: // - version let version = cw2::get_contract_version(deps.as_ref().storage) @@ -226,23 +187,18 @@ fn test_migrate() { assert_eq!(token.owner.as_str(), "owner"); } - Cw721Contract::< - DefaultOptionMetadataExtension, - Empty, - Empty, - DefaultOptionCollectionInfoExtension, - >::default() - .migrate( - deps.as_mut(), - env.clone(), - crate::msg::Cw721MigrateMsg::WithUpdate { - minter: None, - creator: None, - }, - "contract_name", - "contract_version", - ) - .unwrap(); + Cw721Contract::::default() + .migrate( + deps.as_mut(), + env.clone(), + crate::msg::Cw721MigrateMsg::WithUpdate { + minter: None, + creator: None, + }, + "contract_name", + "contract_version", + ) + .unwrap(); // version let version = cw2::get_contract_version(deps.as_ref().storage) @@ -259,14 +215,6 @@ fn test_migrate() { .map(|a| a.into_string()); assert_eq!(minter_ownership, Some("legacy_minter".to_string())); - // assert creator ownership - let creator_ownership = CREATOR - .get_ownership(deps.as_ref().storage) - .unwrap() - .owner - .map(|a| a.into_string()); - assert_eq!(creator_ownership, Some("legacy_minter".to_string())); - // assert collection info let collection_info = contract .query_collection_info(deps.as_ref(), env.clone()) @@ -274,8 +222,6 @@ fn test_migrate() { let legacy_contract_info = CollectionInfo { name: "legacy_name".to_string(), symbol: "legacy_symbol".to_string(), - extension: None, - updated_at: env.block.time, }; assert_eq!(collection_info, legacy_contract_info); From 67522f78127f173674f88650f445ea27b855a23c Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 23 May 2024 19:31:50 +0200 Subject: [PATCH 34/47] cleanup --- build.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build.sh b/build.sh index b7b90ac70..e01309840 100755 --- a/build.sh +++ b/build.sh @@ -1,14 +1,12 @@ #!/bin/bash - -## Compiles and optimizes contracts +# Compiles and optimizes contracts set -o errexit -o nounset -o pipefail command -v shellcheck >/dev/null && shellcheck "$0" -# build Ark proxies cd "$(git rev-parse --show-toplevel)" docker run --rm -v "$(pwd)":/code --platform linux/amd64 \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ cosmwasm/workspace-optimizer:0.15.1 # https://hub.docker.com/r/cosmwasm/workspace-optimizer/tags -ls -al ./artifacts/*wasm \ No newline at end of file +ls -al ./artifacts/*wasm From a5652e266d072694faa90369a3f3472426341b58 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 12:02:21 +0200 Subject: [PATCH 35/47] generate schemas for entry points, cleanup --- Cargo.lock | 154 +- Cargo.toml | 2 +- contracts/cw2981-royalties/src/msg.rs | 8 - contracts/cw721-base/Cargo.toml | 1 + contracts/cw721-base/examples/schema.rs | 23 + .../cw721-base}/schema/execute_msg.json | 0 .../cw721-base}/schema/instantiate_msg.json | 0 contracts/cw721-base/schema/migrate_msg.json | 33 + .../cw721-base}/schema/query_msg.json | 0 contracts/cw721-base/src/state.rs | 2 + contracts/cw721-expiration/examples/schema.rs | 25 +- .../schema/cw721-expiration.json | 1740 ----------------- .../cw721-expiration/schema/execute_msg.json | 562 ++++++ .../schema/instantiate_msg.json | 40 + .../cw721-expiration/schema/migrate_msg.json | 33 + .../cw721-expiration/schema/query_msg.json | 458 +++++ contracts/cw721-expiration/src/msg.rs | 5 +- contracts/cw721-expiration/src/state.rs | 4 + contracts/cw721-fixed-price/src/state.rs | 5 +- .../cw721-non-transferable/examples/schema.rs | 37 +- .../schema/all_nft_info_response.json | 249 --- .../schema/approval_response.json | 105 - .../schema/approvals_response.json | 108 - .../schema/migrate_msg.json | 33 + .../schema/nft_info_response.json | 113 -- .../schema/num_tokens_response.json | 16 - .../schema/operators_response.json | 108 - .../schema/owner_of_response.json | 114 -- .../schema/tokens_response.json | 18 - contracts/cw721-non-transferable/src/msg.rs | 4 +- contracts/cw721-non-transferable/src/state.rs | 3 + packages/cw721/examples/schema.rs | 27 +- .../cw721/schema/collection_info_msg.json | 2 +- packages/cw721/schema/cw721_execute_msg.json | 562 ++++++ .../cw721/schema/cw721_instantiate_msg.json | 33 + packages/cw721/schema/cw721_migrate_msg.json | 33 + packages/cw721/schema/cw721_query_msg.json | 382 ++++ .../cw721}/schema/minter_response.json | 0 packages/cw721/src/execute.rs | 3 +- 39 files changed, 2338 insertions(+), 2707 deletions(-) create mode 100644 contracts/cw721-base/examples/schema.rs rename {packages/cw721 => contracts/cw721-base}/schema/execute_msg.json (100%) rename {packages/cw721 => contracts/cw721-base}/schema/instantiate_msg.json (100%) create mode 100644 contracts/cw721-base/schema/migrate_msg.json rename {packages/cw721 => contracts/cw721-base}/schema/query_msg.json (100%) delete mode 100644 contracts/cw721-expiration/schema/cw721-expiration.json create mode 100644 contracts/cw721-expiration/schema/execute_msg.json create mode 100644 contracts/cw721-expiration/schema/instantiate_msg.json create mode 100644 contracts/cw721-expiration/schema/migrate_msg.json create mode 100644 contracts/cw721-expiration/schema/query_msg.json delete mode 100644 contracts/cw721-non-transferable/schema/all_nft_info_response.json delete mode 100644 contracts/cw721-non-transferable/schema/approval_response.json delete mode 100644 contracts/cw721-non-transferable/schema/approvals_response.json create mode 100644 contracts/cw721-non-transferable/schema/migrate_msg.json delete mode 100644 contracts/cw721-non-transferable/schema/nft_info_response.json delete mode 100644 contracts/cw721-non-transferable/schema/num_tokens_response.json delete mode 100644 contracts/cw721-non-transferable/schema/operators_response.json delete mode 100644 contracts/cw721-non-transferable/schema/owner_of_response.json delete mode 100644 contracts/cw721-non-transferable/schema/tokens_response.json rename contracts/cw721-non-transferable/schema/collection_info.json => packages/cw721/schema/collection_info_msg.json (89%) create mode 100644 packages/cw721/schema/cw721_execute_msg.json create mode 100644 packages/cw721/schema/cw721_instantiate_msg.json create mode 100644 packages/cw721/schema/cw721_migrate_msg.json create mode 100644 packages/cw721/schema/cw721_query_msg.json rename {contracts/cw721-non-transferable => packages/cw721}/schema/minter_response.json (100%) diff --git a/Cargo.lock b/Cargo.lock index 5485d000e..3bad6b0f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "base16ct" @@ -75,9 +75,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cfg-if" @@ -93,9 +93,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cosmwasm-crypto" -version = "1.5.3" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9934c79e58d9676edfd592557dee765d2a6ef54c09d5aa2edb06156b00148966" +checksum = "dd50718a2b6830ce9eb5d465de5a018a12e71729d66b70807ce97e6dd14f931d" dependencies = [ "digest 0.10.7", "ecdsa", @@ -107,18 +107,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.3" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5e72e330bd3bdab11c52b5ecbdeb6a8697a004c57964caeb5d876f0b088b3c" +checksum = "242e98e7a231c122e08f300d9db3262d1007b51758a8732cd6210b3e9faa4f3a" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.3" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e3a2136e2a60e8b6582f5dffca5d1a683ed77bf38537d330bc1dfccd69010" +checksum = "7879036156092ad1c22fe0d7316efc5a5eceec2bc3906462a2560215f2a2f929" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -129,9 +129,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.3" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d803bea6bd9ed61bd1ee0b4a2eb09ee20dbb539cc6e0b8795614d20952ebb1" +checksum = "0bb57855fbfc83327f8445ae0d413b1a05ac0d68c396ab4d122b2abd7bb82cb6" dependencies = [ "proc-macro2", "quote", @@ -140,9 +140,9 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.3" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8666e572a3a2519010dde88c04d16e9339ae751b56b2bb35081fe3f7d6be74" +checksum = "78c1556156fdf892a55cced6115968b961eaaadd6f724a2c2cb7d1e168e32dd3" dependencies = [ "base64", "bech32", @@ -216,16 +216,16 @@ dependencies = [ [[package]] name = "cw-address-like" version = "1.0.4" -source = "git+https://github.com/public-awesome/cw-plus-plus.git?branch=multiple_ownership#d40ce97bc1cc0e4870591ba8bf4a6160b1ae8b00" +source = "git+https://github.com/public-awesome/cw-plus-plus.git?rev=28c1a09bfc6b4f1942fefe3eb0b50faf9d3b1523#28c1a09bfc6b4f1942fefe3eb0b50faf9d3b1523" dependencies = [ "cosmwasm-std", ] [[package]] name = "cw-multi-test" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fff029689ae89127cf6d7655809a68d712f3edbdb9686c70b018ba438b26ca" +checksum = "cc392a5cb7e778e3f90adbf7faa43c4db7f35b6623224b08886d796718edb875" dependencies = [ "anyhow", "bech32", @@ -234,7 +234,7 @@ dependencies = [ "cw-utils 1.0.3", "derivative", "itertools 0.12.1", - "prost 0.12.3", + "prost 0.12.6", "schemars", "serde", "sha2 0.10.8", @@ -259,11 +259,11 @@ dependencies = [ [[package]] name = "cw-ownable" version = "0.6.0" -source = "git+https://github.com/public-awesome/cw-plus-plus.git?branch=multiple_ownership#d40ce97bc1cc0e4870591ba8bf4a6160b1ae8b00" +source = "git+https://github.com/public-awesome/cw-plus-plus.git?rev=28c1a09bfc6b4f1942fefe3eb0b50faf9d3b1523#28c1a09bfc6b4f1942fefe3eb0b50faf9d3b1523" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-address-like 1.0.4 (git+https://github.com/public-awesome/cw-plus-plus.git?branch=multiple_ownership)", + "cw-address-like 1.0.4 (git+https://github.com/public-awesome/cw-plus-plus.git?rev=28c1a09bfc6b4f1942fefe3eb0b50faf9d3b1523)", "cw-ownable-derive 0.6.0", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", @@ -284,7 +284,7 @@ dependencies = [ [[package]] name = "cw-ownable-derive" version = "0.6.0" -source = "git+https://github.com/public-awesome/cw-plus-plus.git?branch=multiple_ownership#d40ce97bc1cc0e4870591ba8bf4a6160b1ae8b00" +source = "git+https://github.com/public-awesome/cw-plus-plus.git?rev=28c1a09bfc6b4f1942fefe3eb0b50faf9d3b1523#28c1a09bfc6b4f1942fefe3eb0b50faf9d3b1523" dependencies = [ "proc-macro2", "quote", @@ -541,6 +541,7 @@ dependencies = [ name = "cw721-base" version = "0.19.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-ownable 0.6.0", "cw2 1.1.2", @@ -615,9 +616,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", @@ -692,9 +693,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "elliptic-curve" @@ -744,9 +745,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -797,15 +798,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -817,9 +809,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "k256" @@ -837,9 +829,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "once_cell" @@ -849,9 +841,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "pkcs8" @@ -865,9 +857,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -884,12 +876,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", - "prost-derive 0.12.3", + "prost-derive 0.12.6", ] [[package]] @@ -907,22 +899,22 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.66", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -954,15 +946,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schemars" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "schemars_derive", @@ -972,14 +964,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.66", ] [[package]] @@ -998,15 +990,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -1022,31 +1014,31 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.66", ] [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.66", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -1122,9 +1114,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -1133,22 +1125,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.66", ] [[package]] @@ -1177,6 +1169,6 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 226184477..9f7ef2f7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ cw721-base-016 = { git = "https://github.com/CosmWasm/cw-nfts", tag = "v0.16.0" cw721-base-017 = { git = "https://github.com/CosmWasm/cw-nfts", tag = "v0.17.0", package = "cw721-base" } # needed for testing legacy migration cw721-base-018 = { git = "https://github.com/CosmWasm/cw-nfts", tag = "v0.18.0", package = "cw721-base" } # needed for testing legacy migration cw-multi-test = "^0.20" -cw-ownable = { git = "https://github.com/public-awesome/cw-plus-plus.git", branch = "multiple_ownership"} # TODO: switch to official https://github.com/larry0x/cw-plus-plus once merged +cw-ownable = { git = "https://github.com/public-awesome/cw-plus-plus.git", rev = "28c1a09bfc6b4f1942fefe3eb0b50faf9d3b1523"} # TODO: switch to official https://github.com/larry0x/cw-plus-plus once merged cw-storage-plus = "^1.1" cw-utils = "^1.0" schemars = "^0.8" diff --git a/contracts/cw2981-royalties/src/msg.rs b/contracts/cw2981-royalties/src/msg.rs index 9fa6bf1a1..a394cf112 100644 --- a/contracts/cw2981-royalties/src/msg.rs +++ b/contracts/cw2981-royalties/src/msg.rs @@ -132,14 +132,6 @@ pub enum QueryMsg { Extension { msg: Extension }, } -// impl Default for Cw2981QueryMsg { -// fn default() -> Self { -// Cw2981QueryMsg::CheckRoyalties {} -// } -// } - -// impl CustomMsg for Cw2981QueryMsg {} - impl From for Cw721QueryMsg { fn from(msg: QueryMsg) -> Cw721QueryMsg { match msg { diff --git a/contracts/cw721-base/Cargo.toml b/contracts/cw721-base/Cargo.toml index 3ba9c18c2..8b0427498 100644 --- a/contracts/cw721-base/Cargo.toml +++ b/contracts/cw721-base/Cargo.toml @@ -24,6 +24,7 @@ backtraces = ["cosmwasm-std/backtraces"] library = [] [dependencies] +cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-ownable = { workspace = true } cw2 = { workspace = true } diff --git a/contracts/cw721-base/examples/schema.rs b/contracts/cw721-base/examples/schema.rs new file mode 100644 index 000000000..9f48bdb49 --- /dev/null +++ b/contracts/cw721-base/examples/schema.rs @@ -0,0 +1,23 @@ +use cosmwasm_schema::{export_schema_with_title, remove_schemas, schema_for}; +use cosmwasm_std::Empty; +use cw721::state::DefaultOptionMetadataExtension; +use cw721_base::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +use std::env::current_dir; +use std::fs::create_dir_all; + +fn main() { + let mut out_dir = current_dir().unwrap(); + out_dir.push("schema"); + create_dir_all(&out_dir).unwrap(); + remove_schemas(&out_dir).unwrap(); + + // entry points - generate always with title for avoiding name suffixes like "..._empty_for_..." due to generics + export_schema_with_title(&schema_for!(InstantiateMsg), &out_dir, "InstantiateMsg"); + export_schema_with_title( + &schema_for!(ExecuteMsg::), + &out_dir, + "ExecuteMsg", + ); + export_schema_with_title(&schema_for!(QueryMsg), &out_dir, "QueryMsg"); + export_schema_with_title(&schema_for!(MigrateMsg), &out_dir, "MigrateMsg"); +} diff --git a/packages/cw721/schema/execute_msg.json b/contracts/cw721-base/schema/execute_msg.json similarity index 100% rename from packages/cw721/schema/execute_msg.json rename to contracts/cw721-base/schema/execute_msg.json diff --git a/packages/cw721/schema/instantiate_msg.json b/contracts/cw721-base/schema/instantiate_msg.json similarity index 100% rename from packages/cw721/schema/instantiate_msg.json rename to contracts/cw721-base/schema/instantiate_msg.json diff --git a/contracts/cw721-base/schema/migrate_msg.json b/contracts/cw721-base/schema/migrate_msg.json new file mode 100644 index 000000000..f83fa9508 --- /dev/null +++ b/contracts/cw721-base/schema/migrate_msg.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "with_update" + ], + "properties": { + "with_update": { + "type": "object", + "properties": { + "creator": { + "type": [ + "string", + "null" + ] + }, + "minter": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/packages/cw721/schema/query_msg.json b/contracts/cw721-base/schema/query_msg.json similarity index 100% rename from packages/cw721/schema/query_msg.json rename to contracts/cw721-base/schema/query_msg.json diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index d90fa91d5..688a50684 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -1,6 +1,8 @@ use cosmwasm_std::CustomMsg; + // expose to all others using contract, so others dont need to import cw721 pub use cw721::state::*; + use serde::de::DeserializeOwned; use serde::Serialize; diff --git a/contracts/cw721-expiration/examples/schema.rs b/contracts/cw721-expiration/examples/schema.rs index 2a2acfe44..0ab40dbc5 100644 --- a/contracts/cw721-expiration/examples/schema.rs +++ b/contracts/cw721-expiration/examples/schema.rs @@ -1,10 +1,23 @@ -use cosmwasm_schema::write_api; +use cosmwasm_schema::{export_schema_with_title, remove_schemas, schema_for}; use cosmwasm_std::Empty; -use cw721_expiration::msg::{InstantiateMsg, QueryMsg}; +use cw721::state::DefaultOptionMetadataExtension; +use cw721_expiration::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +use std::env::current_dir; +use std::fs::create_dir_all; fn main() { - write_api! { - instantiate: InstantiateMsg, - query: QueryMsg, - } + let mut out_dir = current_dir().unwrap(); + out_dir.push("schema"); + create_dir_all(&out_dir).unwrap(); + remove_schemas(&out_dir).unwrap(); + + // entry points - generate always with title for avoiding name suffixes like "..._empty_for_..." due to generics + export_schema_with_title(&schema_for!(InstantiateMsg), &out_dir, "InstantiateMsg"); + export_schema_with_title( + &schema_for!(ExecuteMsg::), + &out_dir, + "ExecuteMsg", + ); + export_schema_with_title(&schema_for!(QueryMsg), &out_dir, "QueryMsg"); + export_schema_with_title(&schema_for!(MigrateMsg), &out_dir, "MigrateMsg"); } diff --git a/contracts/cw721-expiration/schema/cw721-expiration.json b/contracts/cw721-expiration/schema/cw721-expiration.json deleted file mode 100644 index bada44971..000000000 --- a/contracts/cw721-expiration/schema/cw721-expiration.json +++ /dev/null @@ -1,1740 +0,0 @@ -{ - "contract_name": "cw721-expiration", - "contract_version": "0.19.0", - "idl_version": "1.0.0", - "instantiate": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "InstantiateMsg", - "type": "object", - "required": [ - "expiration_days", - "name", - "symbol" - ], - "properties": { - "expiration_days": { - "description": "max 65535 days", - "type": "integer", - "format": "uint16", - "minimum": 0.0 - }, - "minter": { - "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", - "type": [ - "string", - "null" - ] - }, - "name": { - "description": "Name of the NFT contract", - "type": "string" - }, - "symbol": { - "description": "Symbol of the NFT contract", - "type": "string" - }, - "withdraw_address": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "execute": null, - "query": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", - "oneOf": [ - { - "description": "Return the owner of the given token, error if token does not exist", - "type": "object", - "required": [ - "owner_of" - ], - "properties": { - "owner_of": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "include_expired_nft": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return operator that can access all of the owner's tokens.", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "type": "object", - "required": [ - "spender", - "token_id" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "include_expired_nft": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "spender": { - "type": "string" - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return approvals that a token has", - "type": "object", - "required": [ - "approvals" - ], - "properties": { - "approvals": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "include_expired_nft": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", - "type": "object", - "required": [ - "nft_info" - ], - "properties": { - "nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired_nft": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization for clients", - "type": "object", - "required": [ - "all_nft_info" - ], - "properties": { - "all_nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "include_expired_nft": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "include_expired_nft": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With Enumerable extension. Requires pagination. Lists all token_ids controlled by the contract.", - "type": "object", - "required": [ - "all_tokens" - ], - "properties": { - "all_tokens": { - "type": "object", - "properties": { - "include_expired_nft": { - "description": "unset or false will filter out expired nfts, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return approval of a given operator for all tokens of an owner, error if not set", - "type": "object", - "required": [ - "operator" - ], - "properties": { - "operator": { - "type": "object", - "required": [ - "operator", - "owner" - ], - "properties": { - "include_expired": { - "type": [ - "boolean", - "null" - ] - }, - "operator": { - "type": "string" - }, - "owner": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "List all operators that can access all of the owner's tokens", - "type": "object", - "required": [ - "all_operators" - ], - "properties": { - "all_operators": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired items, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Total number of tokens issued, including all expired NFTs", - "type": "object", - "required": [ - "num_tokens" - ], - "properties": { - "num_tokens": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "contract_info" - ], - "properties": { - "contract_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns top-level metadata about the contract", - "type": "object", - "required": [ - "get_collection_info" - ], - "properties": { - "get_collection_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "ownership" - ], - "properties": { - "ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_minter_ownership" - ], - "properties": { - "get_minter_ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return the minter", - "type": "object", - "required": [ - "minter" - ], - "properties": { - "minter": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Extension query", - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "type": "object", - "required": [ - "msg" - ], - "properties": { - "msg": { - "$ref": "#/definitions/Empty" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_withdraw_address" - ], - "properties": { - "get_withdraw_address": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Empty": { - "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", - "type": "object" - } - } - }, - "migrate": null, - "sudo": null, - "responses": { - "all_nft_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AllNftInfoResponse_for_Nullable_Metadata", - "type": "object", - "required": [ - "access", - "info" - ], - "properties": { - "access": { - "description": "Who can transfer the token", - "allOf": [ - { - "$ref": "#/definitions/OwnerOfResponse" - } - ] - }, - "info": { - "description": "Data on the token itself,", - "allOf": [ - { - "$ref": "#/definitions/NftInfoResponse_for_Nullable_Metadata" - } - ] - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Metadata": { - "type": "object", - "properties": { - "animation_url": { - "type": [ - "string", - "null" - ] - }, - "attributes": { - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Trait" - } - }, - "background_color": { - "type": [ - "string", - "null" - ] - }, - "description": { - "type": [ - "string", - "null" - ] - }, - "external_url": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": [ - "string", - "null" - ] - }, - "image_data": { - "type": [ - "string", - "null" - ] - }, - "name": { - "type": [ - "string", - "null" - ] - }, - "youtube_url": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "NftInfoResponse_for_Nullable_Metadata": { - "type": "object", - "properties": { - "extension": { - "description": "You can add any custom metadata here when you extend cw721-base", - "anyOf": [ - { - "$ref": "#/definitions/Metadata" - }, - { - "type": "null" - } - ] - }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "OwnerOfResponse": { - "type": "object", - "required": [ - "approvals", - "owner" - ], - "properties": { - "approvals": { - "description": "If set this address is approved to transfer/send the token as well", - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - }, - "owner": { - "description": "Owner of the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Trait": { - "type": "object", - "required": [ - "trait_type", - "value" - ], - "properties": { - "display_type": { - "type": [ - "string", - "null" - ] - }, - "trait_type": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "additionalProperties": false - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "all_operators": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OperatorsResponse", - "type": "object", - "required": [ - "operators" - ], - "properties": { - "operators": { - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "all_tokens": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "TokensResponse", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "description": "Contains all token_ids in lexicographical ordering If there are more than `limit`, use `start_after` in future queries to achieve pagination.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - }, - "approval": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ApprovalResponse", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "$ref": "#/definitions/Approval" - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "approvals": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ApprovalsResponse", - "type": "object", - "required": [ - "approvals" - ], - "properties": { - "approvals": { - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "contract_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo", - "type": "object", - "required": [ - "name", - "symbol" - ], - "properties": { - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - } - }, - "additionalProperties": false - }, - "extension": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Null", - "type": "null" - }, - "get_collection_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo", - "type": "object", - "required": [ - "name", - "symbol" - ], - "properties": { - "name": { - "type": "string" - }, - "symbol": { - "type": "string" - } - }, - "additionalProperties": false - }, - "get_minter_ownership": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Ownership_for_Addr", - "description": "The contract's ownership info", - "type": "object", - "properties": { - "owner": { - "description": "The contract's current owner. `None` if the ownership has been renounced.", - "anyOf": [ - { - "$ref": "#/definitions/Addr" - }, - { - "type": "null" - } - ] - }, - "pending_expiry": { - "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "pending_owner": { - "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", - "anyOf": [ - { - "$ref": "#/definitions/Addr" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "get_withdraw_address": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Nullable_String", - "type": [ - "string", - "null" - ] - }, - "minter": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "MinterResponse", - "description": "Deprecated: use Cw721QueryMsg::GetMinterOwnership instead! Shows who can mint these tokens.", - "type": "object", - "properties": { - "minter": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "nft_info": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NftInfoResponse_for_Nullable_Metadata", - "type": "object", - "properties": { - "extension": { - "description": "You can add any custom metadata here when you extend cw721-base", - "anyOf": [ - { - "$ref": "#/definitions/Metadata" - }, - { - "type": "null" - } - ] - }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false, - "definitions": { - "Metadata": { - "type": "object", - "properties": { - "animation_url": { - "type": [ - "string", - "null" - ] - }, - "attributes": { - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Trait" - } - }, - "background_color": { - "type": [ - "string", - "null" - ] - }, - "description": { - "type": [ - "string", - "null" - ] - }, - "external_url": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": [ - "string", - "null" - ] - }, - "image_data": { - "type": [ - "string", - "null" - ] - }, - "name": { - "type": [ - "string", - "null" - ] - }, - "youtube_url": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "Trait": { - "type": "object", - "required": [ - "trait_type", - "value" - ], - "properties": { - "display_type": { - "type": [ - "string", - "null" - ] - }, - "trait_type": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "additionalProperties": false - } - } - }, - "num_tokens": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NumTokensResponse", - "type": "object", - "required": [ - "count" - ], - "properties": { - "count": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - "operator": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OperatorResponse", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "$ref": "#/definitions/Approval" - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "owner_of": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OwnerOfResponse", - "type": "object", - "required": [ - "approvals", - "owner" - ], - "properties": { - "approvals": { - "description": "If set this address is approved to transfer/send the token as well", - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - }, - "owner": { - "description": "Owner of the token", - "type": "string" - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "ownership": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Ownership_for_Addr", - "description": "The contract's ownership info", - "type": "object", - "properties": { - "owner": { - "description": "The contract's current owner. `None` if the ownership has been renounced.", - "anyOf": [ - { - "$ref": "#/definitions/Addr" - }, - { - "type": "null" - } - ] - }, - "pending_expiry": { - "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", - "anyOf": [ - { - "$ref": "#/definitions/Expiration" - }, - { - "type": "null" - } - ] - }, - "pending_owner": { - "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", - "anyOf": [ - { - "$ref": "#/definitions/Addr" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } - }, - "tokens": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "TokensResponse", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "description": "Contains all token_ids in lexicographical ordering If there are more than `limit`, use `start_after` in future queries to achieve pagination.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - } - } -} diff --git a/contracts/cw721-expiration/schema/execute_msg.json b/contracts/cw721-expiration/schema/execute_msg.json new file mode 100644 index 000000000..2bcf2fea7 --- /dev/null +++ b/contracts/cw721-expiration/schema/execute_msg.json @@ -0,0 +1,562 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "update_ownership" + ], + "properties": { + "update_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "description": "Transfer is a base message to move a token to another account without triggering actions", + "type": "object", + "required": [ + "transfer_nft" + ], + "properties": { + "transfer_nft": { + "type": "object", + "required": [ + "recipient", + "token_id" + ], + "properties": { + "recipient": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Send is a base message to transfer a token to a contract and trigger an action on the receiving contract.", + "type": "object", + "required": [ + "send_nft" + ], + "properties": { + "send_nft": { + "type": "object", + "required": [ + "contract", + "msg", + "token_id" + ], + "properties": { + "contract": { + "type": "string" + }, + "msg": { + "$ref": "#/definitions/Binary" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Allows operator to transfer / send the token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "type": "object", + "required": [ + "approve" + ], + "properties": { + "approve": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Remove previously granted Approval", + "type": "object", + "required": [ + "revoke" + ], + "properties": { + "revoke": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Allows operator to transfer / send any token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "type": "object", + "required": [ + "approve_all" + ], + "properties": { + "approve_all": { + "type": "object", + "required": [ + "operator" + ], + "properties": { + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "operator": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Remove previously granted ApproveAll permission", + "type": "object", + "required": [ + "revoke_all" + ], + "properties": { + "revoke_all": { + "type": "object", + "required": [ + "operator" + ], + "properties": { + "operator": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Mint a new NFT, can only be called by the contract minter", + "type": "object", + "required": [ + "mint" + ], + "properties": { + "mint": { + "type": "object", + "required": [ + "owner", + "token_id" + ], + "properties": { + "extension": { + "description": "Any custom extension used by this contract", + "anyOf": [ + { + "$ref": "#/definitions/Metadata" + }, + { + "type": "null" + } + ] + }, + "owner": { + "description": "The owner of the newly minter NFT", + "type": "string" + }, + "token_id": { + "description": "Unique ID of the NFT", + "type": "string" + }, + "token_uri": { + "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Burn an NFT the sender has access to", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Extension msg", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Sets address to send withdrawn fees to. Only owner can call this.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Removes the withdraw address, so fees are sent to the contract. Only owner can call this.", + "type": "object", + "required": [ + "remove_withdraw_address" + ], + "properties": { + "remove_withdraw_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Withdraw from the contract to the given address. Anyone can call this, which is okay since withdraw address has been set by owner.", + "type": "object", + "required": [ + "withdraw_funds" + ], + "properties": { + "withdraw_funds": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Action": { + "description": "Actions that can be taken to alter the contract's ownership", + "oneOf": [ + { + "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", + "type": "object", + "required": [ + "transfer_ownership" + ], + "properties": { + "transfer_ownership": { + "type": "object", + "required": [ + "new_owner" + ], + "properties": { + "expiry": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "new_owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", + "type": "string", + "enum": [ + "accept_ownership" + ] + }, + { + "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", + "type": "string", + "enum": [ + "renounce_ownership" + ] + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/cw721-expiration/schema/instantiate_msg.json b/contracts/cw721-expiration/schema/instantiate_msg.json new file mode 100644 index 000000000..f52482235 --- /dev/null +++ b/contracts/cw721-expiration/schema/instantiate_msg.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "expiration_days", + "name", + "symbol" + ], + "properties": { + "expiration_days": { + "description": "max 65535 days", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "minter": { + "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", + "type": [ + "string", + "null" + ] + }, + "name": { + "description": "Name of the NFT contract", + "type": "string" + }, + "symbol": { + "description": "Symbol of the NFT contract", + "type": "string" + }, + "withdraw_address": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false +} diff --git a/contracts/cw721-expiration/schema/migrate_msg.json b/contracts/cw721-expiration/schema/migrate_msg.json new file mode 100644 index 000000000..f83fa9508 --- /dev/null +++ b/contracts/cw721-expiration/schema/migrate_msg.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "with_update" + ], + "properties": { + "with_update": { + "type": "object", + "properties": { + "creator": { + "type": [ + "string", + "null" + ] + }, + "minter": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/cw721-expiration/schema/query_msg.json b/contracts/cw721-expiration/schema/query_msg.json new file mode 100644 index 000000000..8fd419769 --- /dev/null +++ b/contracts/cw721-expiration/schema/query_msg.json @@ -0,0 +1,458 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "description": "Return the owner of the given token, error if token does not exist", + "type": "object", + "required": [ + "owner_of" + ], + "properties": { + "owner_of": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "description": "unset or false will filter out expired approvals, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return operator that can access all of the owner's tokens.", + "type": "object", + "required": [ + "approval" + ], + "properties": { + "approval": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return approvals that a token has", + "type": "object", + "required": [ + "approvals" + ], + "properties": { + "approvals": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", + "type": "object", + "required": [ + "nft_info" + ], + "properties": { + "nft_info": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With MetaData Extension. Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization for clients", + "type": "object", + "required": [ + "all_nft_info" + ], + "properties": { + "all_nft_info": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "description": "unset or false will filter out expired approvals, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", + "type": "object", + "required": [ + "tokens" + ], + "properties": { + "tokens": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With Enumerable extension. Requires pagination. Lists all token_ids controlled by the contract.", + "type": "object", + "required": [ + "all_tokens" + ], + "properties": { + "all_tokens": { + "type": "object", + "properties": { + "include_expired_nft": { + "description": "unset or false will filter out expired nfts, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return approval of a given operator for all tokens of an owner, error if not set", + "type": "object", + "required": [ + "operator" + ], + "properties": { + "operator": { + "type": "object", + "required": [ + "operator", + "owner" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "operator": { + "type": "string" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "List all operators that can access all of the owner's tokens", + "type": "object", + "required": [ + "all_operators" + ], + "properties": { + "all_operators": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "include_expired": { + "description": "unset or false will filter out expired items, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Total number of tokens issued, including all expired NFTs", + "type": "object", + "required": [ + "num_tokens" + ], + "properties": { + "num_tokens": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "contract_info" + ], + "properties": { + "contract_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With MetaData Extension. Returns top-level metadata about the contract", + "type": "object", + "required": [ + "get_collection_info" + ], + "properties": { + "get_collection_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ownership" + ], + "properties": { + "ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_minter_ownership" + ], + "properties": { + "get_minter_ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return the minter", + "type": "object", + "required": [ + "minter" + ], + "properties": { + "minter": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Extension query", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_withdraw_address" + ], + "properties": { + "get_withdraw_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + } + } +} diff --git a/contracts/cw721-expiration/src/msg.rs b/contracts/cw721-expiration/src/msg.rs index 5f5d35a2e..94f69898c 100644 --- a/contracts/cw721-expiration/src/msg.rs +++ b/contracts/cw721-expiration/src/msg.rs @@ -1,9 +1,12 @@ -use crate::{DefaultOptionMetadataExtension, MinterResponse}; +use crate::DefaultOptionMetadataExtension; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::Addr; use cw721::state::CollectionInfo; use cw_ownable::Ownership; +// expose to all others using contract, so others dont need to import cw721 +pub use cw721::msg::{Cw721ExecuteMsg as ExecuteMsg, Cw721MigrateMsg as MigrateMsg, *}; + #[cw_serde] pub struct InstantiateMsg { /// max 65535 days diff --git a/contracts/cw721-expiration/src/state.rs b/contracts/cw721-expiration/src/state.rs index de397b46d..4a4be1311 100644 --- a/contracts/cw721-expiration/src/state.rs +++ b/contracts/cw721-expiration/src/state.rs @@ -1,4 +1,8 @@ use cosmwasm_std::{CustomMsg, Timestamp}; + +// expose to all others using contract, so others dont need to import cw721 +pub use cw721::state::*; + use cw721_base::Cw721Contract; use cw_storage_plus::{Item, Map}; use serde::de::DeserializeOwned; diff --git a/contracts/cw721-fixed-price/src/state.rs b/contracts/cw721-fixed-price/src/state.rs index de45b856e..9179a8cd5 100644 --- a/contracts/cw721-fixed-price/src/state.rs +++ b/contracts/cw721-fixed-price/src/state.rs @@ -1,6 +1,9 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Uint128}; -use cw721::state::DefaultOptionMetadataExtension; + +// expose to all others using contract, so others dont need to import cw721 +pub use cw721::state::*; + use cw_storage_plus::Item; #[cw_serde] diff --git a/contracts/cw721-non-transferable/examples/schema.rs b/contracts/cw721-non-transferable/examples/schema.rs index e3b3fffb2..6db9bbd51 100644 --- a/contracts/cw721-non-transferable/examples/schema.rs +++ b/contracts/cw721-non-transferable/examples/schema.rs @@ -1,17 +1,14 @@ use std::env::current_dir; use std::fs::create_dir_all; -use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; +use cosmwasm_schema::{export_schema_with_title, remove_schemas, schema_for}; use cosmwasm_std::Empty; -use cw721::{ - msg::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, Cw721ExecuteMsg, MinterResponse, - NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, - }, - state::{CollectionInfo, DefaultOptionMetadataExtension}, +use cw721::state::DefaultOptionMetadataExtension; +use cw721_non_transferable::{ + msg::{ExecuteMsg, MigrateMsg}, + InstantiateMsg, QueryMsg, }; -use cw721_non_transferable::{InstantiateMsg, QueryMsg}; fn main() { let mut out_dir = current_dir().unwrap(); @@ -19,29 +16,13 @@ fn main() { create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); + // entry points - generate always with title for avoiding name suffixes like "..._empty_for_..." due to generics export_schema_with_title(&schema_for!(InstantiateMsg), &out_dir, "InstantiateMsg"); export_schema_with_title( - &schema_for!(Cw721ExecuteMsg), + &schema_for!(ExecuteMsg::), &out_dir, "ExecuteMsg", ); - export_schema(&schema_for!(QueryMsg), &out_dir); - export_schema_with_title( - &schema_for!(NftInfoResponse), - &out_dir, - "NftInfoResponse", - ); - export_schema_with_title( - &schema_for!(AllNftInfoResponse), - &out_dir, - "AllNftInfoResponse", - ); - export_schema(&schema_for!(ApprovalResponse), &out_dir); - export_schema(&schema_for!(ApprovalsResponse), &out_dir); - export_schema(&schema_for!(OperatorsResponse), &out_dir); - export_schema_with_title(&schema_for!(CollectionInfo), &out_dir, "CollectionInfo"); - export_schema(&schema_for!(MinterResponse), &out_dir); - export_schema(&schema_for!(NumTokensResponse), &out_dir); - export_schema(&schema_for!(OwnerOfResponse), &out_dir); - export_schema(&schema_for!(TokensResponse), &out_dir); + export_schema_with_title(&schema_for!(QueryMsg), &out_dir, "QueryMsg"); + export_schema_with_title(&schema_for!(MigrateMsg), &out_dir, "MigrateMsg"); } diff --git a/contracts/cw721-non-transferable/schema/all_nft_info_response.json b/contracts/cw721-non-transferable/schema/all_nft_info_response.json deleted file mode 100644 index 8a03851da..000000000 --- a/contracts/cw721-non-transferable/schema/all_nft_info_response.json +++ /dev/null @@ -1,249 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AllNftInfoResponse", - "type": "object", - "required": [ - "access", - "info" - ], - "properties": { - "access": { - "description": "Who can transfer the token", - "allOf": [ - { - "$ref": "#/definitions/OwnerOfResponse" - } - ] - }, - "info": { - "description": "Data on the token itself,", - "allOf": [ - { - "$ref": "#/definitions/NftInfoResponse_for_Nullable_Metadata" - } - ] - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Metadata": { - "type": "object", - "properties": { - "animation_url": { - "type": [ - "string", - "null" - ] - }, - "attributes": { - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Trait" - } - }, - "background_color": { - "type": [ - "string", - "null" - ] - }, - "description": { - "type": [ - "string", - "null" - ] - }, - "external_url": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": [ - "string", - "null" - ] - }, - "image_data": { - "type": [ - "string", - "null" - ] - }, - "name": { - "type": [ - "string", - "null" - ] - }, - "youtube_url": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "NftInfoResponse_for_Nullable_Metadata": { - "type": "object", - "properties": { - "extension": { - "description": "You can add any custom metadata here when you extend cw721-base", - "anyOf": [ - { - "$ref": "#/definitions/Metadata" - }, - { - "type": "null" - } - ] - }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "OwnerOfResponse": { - "type": "object", - "required": [ - "approvals", - "owner" - ], - "properties": { - "approvals": { - "description": "If set this address is approved to transfer/send the token as well", - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - }, - "owner": { - "description": "Owner of the token", - "type": "string" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Trait": { - "type": "object", - "required": [ - "trait_type", - "value" - ], - "properties": { - "display_type": { - "type": [ - "string", - "null" - ] - }, - "trait_type": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "additionalProperties": false - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } -} diff --git a/contracts/cw721-non-transferable/schema/approval_response.json b/contracts/cw721-non-transferable/schema/approval_response.json deleted file mode 100644 index c72b71b2d..000000000 --- a/contracts/cw721-non-transferable/schema/approval_response.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ApprovalResponse", - "type": "object", - "required": [ - "approval" - ], - "properties": { - "approval": { - "$ref": "#/definitions/Approval" - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } -} diff --git a/contracts/cw721-non-transferable/schema/approvals_response.json b/contracts/cw721-non-transferable/schema/approvals_response.json deleted file mode 100644 index 01caf4d31..000000000 --- a/contracts/cw721-non-transferable/schema/approvals_response.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ApprovalsResponse", - "type": "object", - "required": [ - "approvals" - ], - "properties": { - "approvals": { - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } -} diff --git a/contracts/cw721-non-transferable/schema/migrate_msg.json b/contracts/cw721-non-transferable/schema/migrate_msg.json new file mode 100644 index 000000000..f83fa9508 --- /dev/null +++ b/contracts/cw721-non-transferable/schema/migrate_msg.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "with_update" + ], + "properties": { + "with_update": { + "type": "object", + "properties": { + "creator": { + "type": [ + "string", + "null" + ] + }, + "minter": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/cw721-non-transferable/schema/nft_info_response.json b/contracts/cw721-non-transferable/schema/nft_info_response.json deleted file mode 100644 index 0f0859230..000000000 --- a/contracts/cw721-non-transferable/schema/nft_info_response.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NftInfoResponse", - "type": "object", - "properties": { - "extension": { - "description": "You can add any custom metadata here when you extend cw721-base", - "anyOf": [ - { - "$ref": "#/definitions/Metadata" - }, - { - "type": "null" - } - ] - }, - "token_uri": { - "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false, - "definitions": { - "Metadata": { - "type": "object", - "properties": { - "animation_url": { - "type": [ - "string", - "null" - ] - }, - "attributes": { - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Trait" - } - }, - "background_color": { - "type": [ - "string", - "null" - ] - }, - "description": { - "type": [ - "string", - "null" - ] - }, - "external_url": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": [ - "string", - "null" - ] - }, - "image_data": { - "type": [ - "string", - "null" - ] - }, - "name": { - "type": [ - "string", - "null" - ] - }, - "youtube_url": { - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, - "Trait": { - "type": "object", - "required": [ - "trait_type", - "value" - ], - "properties": { - "display_type": { - "type": [ - "string", - "null" - ] - }, - "trait_type": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "additionalProperties": false - } - } -} diff --git a/contracts/cw721-non-transferable/schema/num_tokens_response.json b/contracts/cw721-non-transferable/schema/num_tokens_response.json deleted file mode 100644 index aff5850c8..000000000 --- a/contracts/cw721-non-transferable/schema/num_tokens_response.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NumTokensResponse", - "type": "object", - "required": [ - "count" - ], - "properties": { - "count": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false -} diff --git a/contracts/cw721-non-transferable/schema/operators_response.json b/contracts/cw721-non-transferable/schema/operators_response.json deleted file mode 100644 index 426cd48cc..000000000 --- a/contracts/cw721-non-transferable/schema/operators_response.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OperatorsResponse", - "type": "object", - "required": [ - "operators" - ], - "properties": { - "operators": { - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } -} diff --git a/contracts/cw721-non-transferable/schema/owner_of_response.json b/contracts/cw721-non-transferable/schema/owner_of_response.json deleted file mode 100644 index 36b85a171..000000000 --- a/contracts/cw721-non-transferable/schema/owner_of_response.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "OwnerOfResponse", - "type": "object", - "required": [ - "approvals", - "owner" - ], - "properties": { - "approvals": { - "description": "If set this address is approved to transfer/send the token as well", - "type": "array", - "items": { - "$ref": "#/definitions/Approval" - } - }, - "owner": { - "description": "Owner of the token", - "type": "string" - } - }, - "additionalProperties": false, - "definitions": { - "Addr": { - "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", - "type": "string" - }, - "Approval": { - "type": "object", - "required": [ - "expires", - "spender" - ], - "properties": { - "expires": { - "description": "When the Approval expires (maybe Expiration::never)", - "allOf": [ - { - "$ref": "#/definitions/Expiration" - } - ] - }, - "spender": { - "description": "Account that can transfer/send the token", - "allOf": [ - { - "$ref": "#/definitions/Addr" - } - ] - } - }, - "additionalProperties": false - }, - "Expiration": { - "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", - "oneOf": [ - { - "description": "AtHeight will expire when `env.block.height` >= height", - "type": "object", - "required": [ - "at_height" - ], - "properties": { - "at_height": { - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - }, - { - "description": "AtTime will expire when `env.block.time` >= time", - "type": "object", - "required": [ - "at_time" - ], - "properties": { - "at_time": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - { - "description": "Never will never expire. Used to express the empty variant", - "type": "object", - "required": [ - "never" - ], - "properties": { - "never": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } -} diff --git a/contracts/cw721-non-transferable/schema/tokens_response.json b/contracts/cw721-non-transferable/schema/tokens_response.json deleted file mode 100644 index 4728d37e2..000000000 --- a/contracts/cw721-non-transferable/schema/tokens_response.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "TokensResponse", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "description": "Contains all token_ids in lexicographical ordering If there are more than `limit`, use `start_after` in future queries to achieve pagination.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false -} diff --git a/contracts/cw721-non-transferable/src/msg.rs b/contracts/cw721-non-transferable/src/msg.rs index 1525d5b32..24cf2dd3f 100644 --- a/contracts/cw721-non-transferable/src/msg.rs +++ b/contracts/cw721-non-transferable/src/msg.rs @@ -1,5 +1,7 @@ use cosmwasm_schema::cw_serde; -use cw721::{msg::Cw721QueryMsg, state::DefaultOptionMetadataExtension}; +// expose to all others using contract, so others dont need to import cw721 +pub use cw721::msg::{Cw721ExecuteMsg as ExecuteMsg, Cw721MigrateMsg as MigrateMsg, *}; +use cw721::state::DefaultOptionMetadataExtension; #[cw_serde] pub struct InstantiateMsg { diff --git a/contracts/cw721-non-transferable/src/state.rs b/contracts/cw721-non-transferable/src/state.rs index 594bde6e1..13ef71c10 100644 --- a/contracts/cw721-non-transferable/src/state.rs +++ b/contracts/cw721-non-transferable/src/state.rs @@ -2,6 +2,9 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::Addr; use cw_storage_plus::Item; +// expose to all others using contract, so others dont need to import cw721 +pub use cw721::state::*; + #[cw_serde] pub struct Config { pub admin: Option, diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 659b782da..8fa94b919 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -6,9 +6,10 @@ use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, s use cosmwasm_std::Empty; use cw721::{ msg::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, Cw721ExecuteMsg, - Cw721InstantiateMsg, Cw721QueryMsg, NftInfoResponse, NumTokensResponse, OperatorResponse, - OperatorsResponse, OwnerOfResponse, TokensResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoMsg, + Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, Cw721QueryMsg, MinterResponse, + NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, + TokensResponse, }, receiver::Cw721ReceiveMsg, state::{CollectionInfo, DefaultOptionMetadataExtension}, @@ -19,18 +20,29 @@ fn main() { create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); + // entry points - generate always with title for avoiding name suffixes like "..._empty_for_..." due to generics export_schema_with_title( &schema_for!(Cw721InstantiateMsg), &out_dir, - "InstantiateMsg", + "Cw721InstantiateMsg", ); export_schema_with_title( &schema_for!(Cw721ExecuteMsg::), &out_dir, - "ExecuteMsg", + "Cw721ExecuteMsg", ); - export_schema_with_title(&schema_for!(Cw721QueryMsg), &out_dir, "QueryMsg"); - export_schema(&schema_for!(Cw721ReceiveMsg), &out_dir); + export_schema_with_title( + &schema_for!(Cw721QueryMsg), + &out_dir, + "Cw721QueryMsg", + ); + export_schema_with_title(&schema_for!(Cw721MigrateMsg), &out_dir, "Cw721MigrateMsg"); + + // messages + export_schema_with_title(&schema_for!(Cw721ReceiveMsg), &out_dir, "Cw721ReceiveMsg"); + export_schema(&schema_for!(CollectionInfoMsg), &out_dir); + + // responses export_schema_with_title( &schema_for!(NftInfoResponse), &out_dir, @@ -47,6 +59,7 @@ fn main() { export_schema(&schema_for!(OperatorsResponse), &out_dir); export_schema_with_title(&schema_for!(CollectionInfo), &out_dir, "CollectionInfo"); export_schema(&schema_for!(OwnerOfResponse), &out_dir); + export_schema(&schema_for!(MinterResponse), &out_dir); export_schema(&schema_for!(NumTokensResponse), &out_dir); export_schema(&schema_for!(TokensResponse), &out_dir); } diff --git a/contracts/cw721-non-transferable/schema/collection_info.json b/packages/cw721/schema/collection_info_msg.json similarity index 89% rename from contracts/cw721-non-transferable/schema/collection_info.json rename to packages/cw721/schema/collection_info_msg.json index ebb8e6e8e..060c8f6a4 100644 --- a/contracts/cw721-non-transferable/schema/collection_info.json +++ b/packages/cw721/schema/collection_info_msg.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CollectionInfo", + "title": "CollectionInfoMsg", "type": "object", "required": [ "name", diff --git a/packages/cw721/schema/cw721_execute_msg.json b/packages/cw721/schema/cw721_execute_msg.json new file mode 100644 index 000000000..77cf0910f --- /dev/null +++ b/packages/cw721/schema/cw721_execute_msg.json @@ -0,0 +1,562 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Cw721ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "update_ownership" + ], + "properties": { + "update_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + }, + { + "description": "Transfer is a base message to move a token to another account without triggering actions", + "type": "object", + "required": [ + "transfer_nft" + ], + "properties": { + "transfer_nft": { + "type": "object", + "required": [ + "recipient", + "token_id" + ], + "properties": { + "recipient": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Send is a base message to transfer a token to a contract and trigger an action on the receiving contract.", + "type": "object", + "required": [ + "send_nft" + ], + "properties": { + "send_nft": { + "type": "object", + "required": [ + "contract", + "msg", + "token_id" + ], + "properties": { + "contract": { + "type": "string" + }, + "msg": { + "$ref": "#/definitions/Binary" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Allows operator to transfer / send the token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "type": "object", + "required": [ + "approve" + ], + "properties": { + "approve": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Remove previously granted Approval", + "type": "object", + "required": [ + "revoke" + ], + "properties": { + "revoke": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Allows operator to transfer / send any token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "type": "object", + "required": [ + "approve_all" + ], + "properties": { + "approve_all": { + "type": "object", + "required": [ + "operator" + ], + "properties": { + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "operator": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Remove previously granted ApproveAll permission", + "type": "object", + "required": [ + "revoke_all" + ], + "properties": { + "revoke_all": { + "type": "object", + "required": [ + "operator" + ], + "properties": { + "operator": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Mint a new NFT, can only be called by the contract minter", + "type": "object", + "required": [ + "mint" + ], + "properties": { + "mint": { + "type": "object", + "required": [ + "owner", + "token_id" + ], + "properties": { + "extension": { + "description": "Any custom extension used by this contract", + "anyOf": [ + { + "$ref": "#/definitions/Metadata" + }, + { + "type": "null" + } + ] + }, + "owner": { + "description": "The owner of the newly minter NFT", + "type": "string" + }, + "token_id": { + "description": "Unique ID of the NFT", + "type": "string" + }, + "token_uri": { + "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Burn an NFT the sender has access to", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Extension msg", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Sets address to send withdrawn fees to. Only owner can call this.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Removes the withdraw address, so fees are sent to the contract. Only owner can call this.", + "type": "object", + "required": [ + "remove_withdraw_address" + ], + "properties": { + "remove_withdraw_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Withdraw from the contract to the given address. Anyone can call this, which is okay since withdraw address has been set by owner.", + "type": "object", + "required": [ + "withdraw_funds" + ], + "properties": { + "withdraw_funds": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Action": { + "description": "Actions that can be taken to alter the contract's ownership", + "oneOf": [ + { + "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", + "type": "object", + "required": [ + "transfer_ownership" + ], + "properties": { + "transfer_ownership": { + "type": "object", + "required": [ + "new_owner" + ], + "properties": { + "expiry": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "new_owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", + "type": "string", + "enum": [ + "accept_ownership" + ] + }, + { + "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", + "type": "string", + "enum": [ + "renounce_ownership" + ] + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Metadata": { + "type": "object", + "properties": { + "animation_url": { + "type": [ + "string", + "null" + ] + }, + "attributes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Trait" + } + }, + "background_color": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "external_url": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "image_data": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "youtube_url": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Trait": { + "type": "object", + "required": [ + "trait_type", + "value" + ], + "properties": { + "display_type": { + "type": [ + "string", + "null" + ] + }, + "trait_type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/packages/cw721/schema/cw721_instantiate_msg.json b/packages/cw721/schema/cw721_instantiate_msg.json new file mode 100644 index 000000000..337caa3c9 --- /dev/null +++ b/packages/cw721/schema/cw721_instantiate_msg.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Cw721InstantiateMsg", + "type": "object", + "required": [ + "name", + "symbol" + ], + "properties": { + "minter": { + "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", + "type": [ + "string", + "null" + ] + }, + "name": { + "description": "Name of the NFT contract", + "type": "string" + }, + "symbol": { + "description": "Symbol of the NFT contract", + "type": "string" + }, + "withdraw_address": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false +} diff --git a/packages/cw721/schema/cw721_migrate_msg.json b/packages/cw721/schema/cw721_migrate_msg.json new file mode 100644 index 000000000..4bc17cd39 --- /dev/null +++ b/packages/cw721/schema/cw721_migrate_msg.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Cw721MigrateMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "with_update" + ], + "properties": { + "with_update": { + "type": "object", + "properties": { + "creator": { + "type": [ + "string", + "null" + ] + }, + "minter": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/packages/cw721/schema/cw721_query_msg.json b/packages/cw721/schema/cw721_query_msg.json new file mode 100644 index 000000000..0323ea65c --- /dev/null +++ b/packages/cw721/schema/cw721_query_msg.json @@ -0,0 +1,382 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Cw721QueryMsg", + "oneOf": [ + { + "description": "Return the owner of the given token, error if token does not exist", + "type": "object", + "required": [ + "owner_of" + ], + "properties": { + "owner_of": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "description": "unset or false will filter out expired approvals, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return operator that can access all of the owner's tokens.", + "type": "object", + "required": [ + "approval" + ], + "properties": { + "approval": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return approvals that a token has", + "type": "object", + "required": [ + "approvals" + ], + "properties": { + "approvals": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return approval of a given operator for all tokens of an owner, error if not set", + "type": "object", + "required": [ + "operator" + ], + "properties": { + "operator": { + "type": "object", + "required": [ + "operator", + "owner" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "operator": { + "type": "string" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "List all operators that can access all of the owner's tokens", + "type": "object", + "required": [ + "all_operators" + ], + "properties": { + "all_operators": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "include_expired": { + "description": "unset or false will filter out expired items, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Total number of tokens issued", + "type": "object", + "required": [ + "num_tokens" + ], + "properties": { + "num_tokens": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "contract_info" + ], + "properties": { + "contract_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ownership" + ], + "properties": { + "ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract", + "type": "object", + "required": [ + "nft_info" + ], + "properties": { + "nft_info": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With MetaData Extension. Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization for clients", + "type": "object", + "required": [ + "all_nft_info" + ], + "properties": { + "all_nft_info": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "description": "unset or false will filter out expired approvals, you must set to true to see them", + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", + "type": "object", + "required": [ + "tokens" + ], + "properties": { + "tokens": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "With Enumerable extension. Requires pagination. Lists all token_ids controlled by the contract.", + "type": "object", + "required": [ + "all_tokens" + ], + "properties": { + "all_tokens": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return the minter", + "type": "object", + "required": [ + "minter" + ], + "properties": { + "minter": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_withdraw_address" + ], + "properties": { + "get_withdraw_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Do not use - dummy extension query, needed for inferring type parameter during compile", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + } + } +} diff --git a/contracts/cw721-non-transferable/schema/minter_response.json b/packages/cw721/schema/minter_response.json similarity index 100% rename from contracts/cw721-non-transferable/schema/minter_response.json rename to packages/cw721/schema/minter_response.json diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index f65bc5623..556952e1b 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -348,7 +348,8 @@ pub trait Cw721Execute< info: MessageInfo, action: Action, ) -> Result, Cw721ContractError> { - let ownership = MINTER.update_ownership(deps, &env.block, &info.sender, action)?; + let ownership = + MINTER.update_ownership(deps.api, deps.storage, &env.block, &info.sender, action)?; Ok(Response::new() .add_attribute("update_minter_ownership", info.sender) .add_attributes(ownership.into_attributes())) From e02c7f92321dcba8688a6d1d97e31a4e8002d0c5 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 15:10:27 +0200 Subject: [PATCH 36/47] update rustc 1.65 -> 1.71 --- .circleci/config.yml | 38 +++++++++++++++++----------------- Cargo.toml | 2 +- contracts/cw721-base/README.md | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 479bafbd7..2345c74fb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,7 +22,7 @@ workflows: jobs: contract_cw721_base: docker: - - image: rust:1.65.0 + - image: rust:1.71.0 working_directory: ~/project/contracts/cw721-base steps: - checkout: @@ -32,7 +32,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-cw721-base-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-cw721-base-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -42,11 +42,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-base-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-base-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} contract_cw721_expiration: docker: - - image: rust:1.65.0 + - image: rust:1.71.0 working_directory: ~/project/contracts/cw721-expiration steps: - checkout: @@ -56,7 +56,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-cw721-expiration-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-cw721-expiration-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -78,11 +78,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-expiration-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-expiration-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} contract_cw721_fixed_price: docker: - - image: rust:1.65.0 + - image: rust:1.71.0 working_directory: ~/project/contracts/cw721-fixed-price steps: - checkout: @@ -92,7 +92,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-cw721-fixed-price-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-cw721-fixed-price-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -114,11 +114,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-fixed-price-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-fixed-price-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} contract_cw721_receiver_tester: docker: - - image: rust:1.65.0 + - image: rust:1.71.0 working_directory: ~/project/contracts/cw721-receiver-tester steps: - checkout: @@ -128,7 +128,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-cw721-receiver-tester-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-cw721-receiver-tester-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -150,11 +150,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-receiver-tester-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-receiver-tester-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} package_cw721: docker: - - image: rust:1.65.0 + - image: rust:1.71.0 working_directory: ~/project/packages/cw721 steps: - checkout: @@ -191,7 +191,7 @@ jobs: lint: docker: - - image: rust:1.65.0 + - image: rust:1.71.0 steps: - checkout - run: @@ -199,7 +199,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-lint-rust:1.65.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-lint-rust:1.71.0-{{ checksum "Cargo.lock" }} - run: name: Add rustfmt component command: rustup component add rustfmt @@ -218,7 +218,7 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-lint-rust:1.65.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-lint-rust:1.71.0-{{ checksum "Cargo.lock" }} # This runs one time on the top level to ensure all contracts compile properly into wasm. # We don't run the wasm build per contract build, and then reuse a lot of the same dependencies, so this speeds up CI time @@ -226,7 +226,7 @@ jobs: # We also sanity-check the resultant wasm files. wasm-build: docker: - - image: rust:1.65.0 + - image: rust:1.71.0 steps: - checkout: path: ~/project @@ -235,7 +235,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-wasm-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-wasm-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Add wasm32 target command: rustup target add wasm32-unknown-unknown @@ -255,7 +255,7 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-wasm-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-wasm-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Check wasm contracts command: cosmwasm-check ./target/wasm32-unknown-unknown/release/*.wasm diff --git a/Cargo.toml b/Cargo.toml index 9f7ef2f7d..86fba0d47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" repository = "https://github.com/CosmWasm/cw-nfts" homepage = "https://cosmwasm.com" documentation = "https://docs.cosmwasm.com" -rust-version = "1.65" +rust-version = "1.71" [workspace.dependencies] cosmwasm-schema = "^1.2" diff --git a/contracts/cw721-base/README.md b/contracts/cw721-base/README.md index d3be359b9..41ddf5228 100644 --- a/contracts/cw721-base/README.md +++ b/contracts/cw721-base/README.md @@ -32,7 +32,7 @@ If provided, it is expected that the _token_uri_ points to a JSON file following ## Running this contract -You will need Rust 1.65+ with `wasm32-unknown-unknown` target installed. +You will need Rust 1.71+ with `wasm32-unknown-unknown` target installed. You can run unit tests on this via: From 6b3af843468dab01ba7eb67d7dda098dde50291e Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 15:26:05 +0200 Subject: [PATCH 37/47] update cosmwasm version 1.2 -> 1.5, due to rustc compiler issues see also: https://github.com/CosmWasm/cosmwasm/issues/1727 --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 86fba0d47..035356c93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,8 @@ documentation = "https://docs.cosmwasm.com" rust-version = "1.71" [workspace.dependencies] -cosmwasm-schema = "^1.2" -cosmwasm-std = "^1.2" +cosmwasm-schema = "^1.5" +cosmwasm-std = "^1.5" cw2 = "^1.1" cw20 = "^1.1" cw721 = { version = "*", path = "./packages/cw721" } From 80a2d1763c9d7b1e8c6e49940d8bae72e3aea8ad Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 15:30:00 +0200 Subject: [PATCH 38/47] update optimizer --- .circleci/config.yml | 2 +- build.sh | 2 +- contracts/cw721-expiration/README.md | 2 +- contracts/cw721-fixed-price/README.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2345c74fb..159091345 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -286,7 +286,7 @@ jobs: - run: name: Build development contracts command: | - docker run --volumes-from with_code cosmwasm/workspace-optimizer:0.12.13 + docker run --volumes-from with_code cosmwasm/workspace-optimizer:0.16.0 docker cp with_code:/code/artifacts ./artifacts - run: name: Show data diff --git a/build.sh b/build.sh index e01309840..df1d9651c 100755 --- a/build.sh +++ b/build.sh @@ -8,5 +8,5 @@ cd "$(git rev-parse --show-toplevel)" docker run --rm -v "$(pwd)":/code --platform linux/amd64 \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/workspace-optimizer:0.15.1 # https://hub.docker.com/r/cosmwasm/workspace-optimizer/tags + cosmwasm/workspace-optimizer:0.16.0 # https://hub.docker.com/r/cosmwasm/workspace-optimizer/tags ls -al ./artifacts/*wasm diff --git a/contracts/cw721-expiration/README.md b/contracts/cw721-expiration/README.md index d12c11c8c..43bf5c961 100644 --- a/contracts/cw721-expiration/README.md +++ b/contracts/cw721-expiration/README.md @@ -52,7 +52,7 @@ To generate an optimized build run: docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.14.0 + cosmwasm/rust-optimizer:0.16.0 ``` ### Testing diff --git a/contracts/cw721-fixed-price/README.md b/contracts/cw721-fixed-price/README.md index af3256fc2..58baabc32 100644 --- a/contracts/cw721-fixed-price/README.md +++ b/contracts/cw721-fixed-price/README.md @@ -25,7 +25,7 @@ To generate an optimized build run: docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.3 + cosmwasm/rust-optimizer:0.16.0 ``` ### Testing From 0515aa99dc2a990639d1f6115ab434f800704630 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 19:40:33 +0200 Subject: [PATCH 39/47] update rustc 1.71 -> 1.78 --- .circleci/config.yml | 38 +++++++++++++++++----------------- Cargo.toml | 2 +- contracts/cw721-base/README.md | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 159091345..75abacdb7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,7 +22,7 @@ workflows: jobs: contract_cw721_base: docker: - - image: rust:1.71.0 + - image: rust:1.78.0 working_directory: ~/project/contracts/cw721-base steps: - checkout: @@ -32,7 +32,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-cw721-base-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-cw721-base-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -42,11 +42,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-base-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-base-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} contract_cw721_expiration: docker: - - image: rust:1.71.0 + - image: rust:1.78.0 working_directory: ~/project/contracts/cw721-expiration steps: - checkout: @@ -56,7 +56,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-cw721-expiration-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-cw721-expiration-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -78,11 +78,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-expiration-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-expiration-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} contract_cw721_fixed_price: docker: - - image: rust:1.71.0 + - image: rust:1.78.0 working_directory: ~/project/contracts/cw721-fixed-price steps: - checkout: @@ -92,7 +92,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-cw721-fixed-price-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-cw721-fixed-price-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -114,11 +114,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-fixed-price-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-fixed-price-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} contract_cw721_receiver_tester: docker: - - image: rust:1.71.0 + - image: rust:1.78.0 working_directory: ~/project/contracts/cw721-receiver-tester steps: - checkout: @@ -128,7 +128,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-cw721-receiver-tester-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-cw721-receiver-tester-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -150,11 +150,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-cw721-receiver-tester-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-cw721-receiver-tester-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} package_cw721: docker: - - image: rust:1.71.0 + - image: rust:1.78.0 working_directory: ~/project/packages/cw721 steps: - checkout: @@ -191,7 +191,7 @@ jobs: lint: docker: - - image: rust:1.71.0 + - image: rust:1.78.0 steps: - checkout - run: @@ -199,7 +199,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-lint-rust:1.71.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-lint-rust:1.78.0-{{ checksum "Cargo.lock" }} - run: name: Add rustfmt component command: rustup component add rustfmt @@ -218,7 +218,7 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-lint-rust:1.71.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-lint-rust:1.78.0-{{ checksum "Cargo.lock" }} # This runs one time on the top level to ensure all contracts compile properly into wasm. # We don't run the wasm build per contract build, and then reuse a lot of the same dependencies, so this speeds up CI time @@ -226,7 +226,7 @@ jobs: # We also sanity-check the resultant wasm files. wasm-build: docker: - - image: rust:1.71.0 + - image: rust:1.78.0 steps: - checkout: path: ~/project @@ -235,7 +235,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-wasm-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-wasm-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Add wasm32 target command: rustup target add wasm32-unknown-unknown @@ -255,7 +255,7 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-wasm-rust:1.71.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-wasm-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Check wasm contracts command: cosmwasm-check ./target/wasm32-unknown-unknown/release/*.wasm diff --git a/Cargo.toml b/Cargo.toml index 035356c93..8e8bea930 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" repository = "https://github.com/CosmWasm/cw-nfts" homepage = "https://cosmwasm.com" documentation = "https://docs.cosmwasm.com" -rust-version = "1.71" +rust-version = "1.78" [workspace.dependencies] cosmwasm-schema = "^1.5" diff --git a/contracts/cw721-base/README.md b/contracts/cw721-base/README.md index 41ddf5228..6ab157023 100644 --- a/contracts/cw721-base/README.md +++ b/contracts/cw721-base/README.md @@ -32,7 +32,7 @@ If provided, it is expected that the _token_uri_ points to a JSON file following ## Running this contract -You will need Rust 1.71+ with `wasm32-unknown-unknown` target installed. +You will need Rust 1.78+ with `wasm32-unknown-unknown` target installed. You can run unit tests on this via: From 6944bcb8356553c4ee146b7e82f7f23b6077aca3 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 21:42:22 +0200 Subject: [PATCH 40/47] use optimizer --- .circleci/config.yml | 49 +++++++++++++++++++++++++++++--------------- Makefile.toml | 2 +- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 75abacdb7..348723f2f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -226,27 +226,42 @@ jobs: # We also sanity-check the resultant wasm files. wasm-build: docker: - - image: rust:1.78.0 + # Image from https://github.com/cibuilds/github, based on alpine + - image: cibuilds/github:0.13 steps: - - checkout: - path: ~/project - run: - name: Version information - command: rustc --version; cargo --version; rustup --version - - restore_cache: - keys: - - cargocache-wasm-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} + name: Install Docker client + command: apk add docker-cli + - setup_remote_docker + - checkout + - run: + # We cannot mount local folders, see https://circleci.com/docs/2.0/building-docker-images/#mounting-folders + name: Prepare volume with source code + command: | + # create a dummy container which will hold a volume with config + docker create -v /code --name with_code alpine /bin/true + # copy a config file into this volume + docker cp Cargo.toml with_code:/code + docker cp Cargo.lock with_code:/code + # copy code into this volume + docker cp ./contracts with_code:/code + docker cp ./packages with_code:/code - run: - name: Add wasm32 target - command: rustup target add wasm32-unknown-unknown + name: Build development contracts + command: | + docker run --volumes-from with_code cosmwasm/workspace-optimizer:0.16.0 + docker cp with_code:/code/artifacts ./artifacts - run: - name: Build Wasm Release + name: List artifacts and checksums command: | - for C in ./contracts/*/ - do - echo "Compiling `basename $C`..." - (cd $C && cargo build --release --target wasm32-unknown-unknown --lib --locked) - done + ls -l artifacts + cat artifacts/checksums.txt + - run: + name: Install dependencies + command: | + curl https://sh.rustup.rs -sSf | sh -s -- -y + source $HOME/.cargo/env + rustup target add wasm32-unknown-unknown - run: name: Install check_contract # Uses --debug for compilation speed @@ -258,7 +273,7 @@ jobs: key: cargocache-wasm-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Check wasm contracts - command: cosmwasm-check ./target/wasm32-unknown-unknown/release/*.wasm + command: cosmwasm-check ./artifacts/*.wasm # This job roughly follows the instructions from https://circleci.com/blog/publishing-to-github-releases-via-circleci/ build_and_upload_contracts: diff --git a/Makefile.toml b/Makefile.toml index 951218c6c..633e010ed 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -32,7 +32,7 @@ fi docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - ${image}:0.15.0 + ${image}:0.16.0 """ [tasks.schema] From f135fe8f61649d82618ee67b44982190a7cffc71 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 21:52:08 +0200 Subject: [PATCH 41/47] formatting --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 348723f2f..dbfc04c4f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -257,11 +257,11 @@ jobs: ls -l artifacts cat artifacts/checksums.txt - run: - name: Install dependencies - command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y - source $HOME/.cargo/env - rustup target add wasm32-unknown-unknown + name: Install dependencies + command: | + curl https://sh.rustup.rs -sSf | sh -s -- -y + source $HOME/.cargo/env + rustup target add wasm32-unknown-unknown - run: name: Install check_contract # Uses --debug for compilation speed From 61d369365d880c745c10d74b9a6e065273e1170c Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 21:58:33 +0200 Subject: [PATCH 42/47] install rustup first --- .circleci/config.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dbfc04c4f..a0a5b1b9c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -234,6 +234,16 @@ jobs: command: apk add docker-cli - setup_remote_docker - checkout + - run: + name: Install dependencies + command: | + curl https://sh.rustup.rs -sSf | sh -s -- -y + source $HOME/.cargo/env + rustup target add wasm32-unknown-unknown + - run: + name: Install check_contract + # Uses --debug for compilation speed + command: cargo install --debug --version 1.1.0 --locked -- cosmwasm-check - run: # We cannot mount local folders, see https://circleci.com/docs/2.0/building-docker-images/#mounting-folders name: Prepare volume with source code @@ -247,7 +257,7 @@ jobs: docker cp ./contracts with_code:/code docker cp ./packages with_code:/code - run: - name: Build development contracts + name: Build and optimize contracts command: | docker run --volumes-from with_code cosmwasm/workspace-optimizer:0.16.0 docker cp with_code:/code/artifacts ./artifacts @@ -256,16 +266,6 @@ jobs: command: | ls -l artifacts cat artifacts/checksums.txt - - run: - name: Install dependencies - command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y - source $HOME/.cargo/env - rustup target add wasm32-unknown-unknown - - run: - name: Install check_contract - # Uses --debug for compilation speed - command: cargo install --debug --version 1.1.0 --locked -- cosmwasm-check - save_cache: paths: - /usr/local/cargo/registry From d4ff6e75d0ce5f79a8a0edfcb894b2b930f12ed4 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 22:03:10 +0200 Subject: [PATCH 43/47] set $HOME/.cargo/env --- .circleci/config.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a0a5b1b9c..e0536f21e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -241,9 +241,12 @@ jobs: source $HOME/.cargo/env rustup target add wasm32-unknown-unknown - run: - name: Install check_contract + name: Install cosmwasm-check # Uses --debug for compilation speed - command: cargo install --debug --version 1.1.0 --locked -- cosmwasm-check + command: | + source $HOME/.cargo/env + cargo install --debug --version 1.1.0 --locked cosmwasm-check + - run: # We cannot mount local folders, see https://circleci.com/docs/2.0/building-docker-images/#mounting-folders name: Prepare volume with source code @@ -273,7 +276,9 @@ jobs: key: cargocache-wasm-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Check wasm contracts - command: cosmwasm-check ./artifacts/*.wasm + command: | + source $HOME/.cargo/env + cosmwasm-check ./artifacts/*.wasm # This job roughly follows the instructions from https://circleci.com/blog/publishing-to-github-releases-via-circleci/ build_and_upload_contracts: From e34879f77f51a00c7949aebda80bb13a3f9b10a2 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 22:09:39 +0200 Subject: [PATCH 44/47] install libgcc --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e0536f21e..593f7c620 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -231,7 +231,7 @@ jobs: steps: - run: name: Install Docker client - command: apk add docker-cli + command: apk add docker-cli libgcc - setup_remote_docker - checkout - run: From 410d11e45d0a5dd9469716fa7c44a7fc697e9ac1 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 22:16:55 +0200 Subject: [PATCH 45/47] install build-base --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 593f7c620..eb039fd50 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -231,7 +231,7 @@ jobs: steps: - run: name: Install Docker client - command: apk add docker-cli libgcc + command: apk add docker-cli libgcc build-base - setup_remote_docker - checkout - run: From 495cccbaa6fda7577a2dde20880c9cc060672333 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 22:23:52 +0200 Subject: [PATCH 46/47] cleanup --- .circleci/config.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index eb039fd50..1add64d51 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -230,8 +230,8 @@ jobs: - image: cibuilds/github:0.13 steps: - run: - name: Install Docker client - command: apk add docker-cli libgcc build-base + name: Install Docker client, rust/cargo libs (build-base) + command: apk add docker-cli build-base - setup_remote_docker - checkout - run: @@ -269,11 +269,6 @@ jobs: command: | ls -l artifacts cat artifacts/checksums.txt - - save_cache: - paths: - - /usr/local/cargo/registry - - target - key: cargocache-wasm-rust:1.78.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Check wasm contracts command: | From 33c7abcb708148d2cc1861fe4072ed5e41082593 Mon Sep 17 00:00:00 2001 From: mr-t Date: Thu, 13 Jun 2024 22:31:33 +0200 Subject: [PATCH 47/47] cleanup --- .circleci/config.yml | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1add64d51..856d6d100 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -234,19 +234,6 @@ jobs: command: apk add docker-cli build-base - setup_remote_docker - checkout - - run: - name: Install dependencies - command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y - source $HOME/.cargo/env - rustup target add wasm32-unknown-unknown - - run: - name: Install cosmwasm-check - # Uses --debug for compilation speed - command: | - source $HOME/.cargo/env - cargo install --debug --version 1.1.0 --locked cosmwasm-check - - run: # We cannot mount local folders, see https://circleci.com/docs/2.0/building-docker-images/#mounting-folders name: Prepare volume with source code @@ -269,6 +256,18 @@ jobs: command: | ls -l artifacts cat artifacts/checksums.txt + - run: + name: Install Rust and Cargo + command: | + curl https://sh.rustup.rs -sSf | sh -s -- -y + source $HOME/.cargo/env + rustup target add wasm32-unknown-unknown + - run: + name: Install cosmwasm-check + # Uses --debug for compilation speed + command: | + source $HOME/.cargo/env + cargo install --debug --version 1.1.0 --locked cosmwasm-check - run: name: Check wasm contracts command: |