From df468b29b1ed04ab013ebe3fda0fd8e44e090e96 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 12:41:46 +0200 Subject: [PATCH 01/13] deps: Option -> deps: Deps, env: Option<&Env> -> env: &Env --- contracts/cw2981-royalties/src/msg.rs | 10 ++--- packages/cw721/src/error.rs | 6 --- packages/cw721/src/execute.rs | 13 +++---- packages/cw721/src/msg.rs | 56 ++++++++++++--------------- packages/cw721/src/state.rs | 16 ++++---- packages/cw721/src/traits.rs | 22 +++++------ 6 files changed, 53 insertions(+), 70 deletions(-) diff --git a/contracts/cw2981-royalties/src/msg.rs b/contracts/cw2981-royalties/src/msg.rs index 2cce20b8a..49f5a95d3 100644 --- a/contracts/cw2981-royalties/src/msg.rs +++ b/contracts/cw2981-royalties/src/msg.rs @@ -245,8 +245,8 @@ pub type MetadataWithRoyaltyMsg = MetadataWithRoyalty; impl StateFactory for MetadataWithRoyaltyMsg { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&MetadataWithRoyalty>, ) -> Result { @@ -303,8 +303,8 @@ impl StateFactory for MetadataWithRoyaltyMsg { fn validate( &self, - deps: Option, - _env: Option<&Env>, + deps: Deps, + _env: &Env, info: Option<&MessageInfo>, current: Option<&MetadataWithRoyalty>, ) -> Result<(), Cw721ContractError> { @@ -312,7 +312,6 @@ impl StateFactory for MetadataWithRoyaltyMsg { // - creator and minter can create NFT metadata // - only creator can update NFT metadata if current.is_none() { - let deps = deps.ok_or(Cw721ContractError::NoDeps)?; let info = info.ok_or(Cw721ContractError::NoInfo)?; // current is none: minter and creator can create new NFT metadata let minter_check = assert_minter(deps.storage, &info.sender); @@ -321,7 +320,6 @@ impl StateFactory for MetadataWithRoyaltyMsg { return Err(Cw721ContractError::NotMinterOrCreator {}); } } else { - let deps = deps.ok_or(Cw721ContractError::NoDeps)?; let info = info.ok_or(Cw721ContractError::NoInfo)?; // current is some: only creator can update NFT metadata assert_creator(deps.storage, &info.sender)?; diff --git a/packages/cw721/src/error.rs b/packages/cw721/src/error.rs index 380ca6f37..04c710721 100644 --- a/packages/cw721/src/error.rs +++ b/packages/cw721/src/error.rs @@ -76,12 +76,6 @@ pub enum Cw721ContractError { #[error("Trait display type in metadata must not be empty")] TraitDisplayTypeEmpty {}, - #[error("Internal error. Missing argument: Deps")] - NoDeps, - #[error("Internal error. Missing argument: Info")] NoInfo, - - #[error("Internal error. Missing argument: Env")] - NoEnv, } diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index a99f80295..a293c037f 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -60,8 +60,7 @@ where symbol: Some(msg.symbol), extension: msg.collection_info_extension, }; - let collection_info = - collection_metadata_msg.create(deps.as_ref().into(), env.into(), info.into(), None)?; + let collection_info = collection_metadata_msg.create(deps.as_ref(), env, info.into(), None)?; let extension_attributes = collection_info.extension.to_attributes_state()?; let collection_info = collection_info.into(); config @@ -316,7 +315,7 @@ pub fn burn_nft( pub fn update_collection_info( deps: DepsMut, info: Option<&MessageInfo>, - env: Option<&Env>, + env: &Env, msg: CollectionInfoMsg, ) -> Result, Cw721ContractError> where @@ -326,7 +325,7 @@ where { let config = Cw721Config::>::default(); let current = query_collection_info_and_extension::(deps.as_ref())?; - let collection_info = msg.create(deps.as_ref().into(), env, info, Some(¤t))?; + let collection_info = msg.create(deps.as_ref(), env, info, Some(¤t))?; let extension_attributes = collection_info.extension.to_attributes_state()?; config .collection_info @@ -367,7 +366,7 @@ where token_uri: token_uri.clone(), extension, }; - let token = token_msg.create(deps.as_ref().into(), env.into(), info.into(), None)?; + let token = token_msg.create(deps.as_ref(), env, info.into(), None)?; let config = Cw721Config::::default(); config .nft_info @@ -419,7 +418,7 @@ pub fn update_creator_ownership( /// NOTE: approvals and owner are not affected by this call, since they belong to the NFT owner. pub fn update_nft_info( deps: DepsMut, - env: Option<&Env>, + env: &Env, info: Option<&MessageInfo>, token_id: String, token_uri: Option, @@ -438,7 +437,7 @@ where token_uri, extension: msg, }; - let updated = nft_info_msg.create(deps.as_ref().into(), env, info, Some(¤t_nft_info))?; + let updated = nft_info_msg.create(deps.as_ref(), env, info, Some(¤t_nft_info))?; contract.nft_info.save(deps.storage, &token_id, &updated)?; Ok(Response::new() .add_attribute("action", "update_nft_info") diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index c7127b7c9..1f368e703 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -386,8 +386,8 @@ impl StateFactory> /// NOTE: In case `info` is not provided (like for migration), creator/minter assertion is skipped. fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&CollectionExtension>, ) -> Result, Cw721ContractError> { @@ -455,12 +455,11 @@ impl StateFactory> /// NOTE: In case `info` is not provided (like for migration), creator/minter assertion is skipped. fn validate( &self, - deps: Option, - _env: Option<&Env>, + deps: Deps, + _env: &Env, info: Option<&MessageInfo>, _current: Option<&CollectionExtension>, ) -> Result<(), Cw721ContractError> { - let deps = deps.ok_or(Cw721ContractError::NoDeps)?; let sender = info.map(|i| i.sender.clone()); // start trading time can only be updated by minter let minter_initialized = MINTER.item.may_load(deps.storage)?; @@ -523,13 +522,12 @@ impl Cw721CustomMsg for RoyaltyInfoResponse {} impl StateFactory for RoyaltyInfoResponse { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&RoyaltyInfo>, ) -> Result { self.validate(deps, env, info, current)?; - let deps = deps.ok_or(Cw721ContractError::NoDeps)?; match current { // Some: update existing royalty info Some(current) => { @@ -551,8 +549,8 @@ impl StateFactory for RoyaltyInfoResponse { fn validate( &self, - _deps: Option, - _env: Option<&Env>, + _deps: Deps, + _env: &Env, _info: Option<&MessageInfo>, current: Option<&RoyaltyInfo>, ) -> Result<(), Cw721ContractError> { @@ -627,8 +625,8 @@ where { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&CollectionInfoAndExtensionResponse>, ) -> Result, Cw721ContractError> { @@ -653,7 +651,6 @@ where // None: create new metadata None => { let extension = self.extension.create(deps, env, info, None)?; - let env = env.ok_or(Cw721ContractError::NoEnv)?; let new = CollectionInfoAndExtensionResponse { name: self.name.clone().unwrap(), symbol: self.symbol.clone().unwrap(), @@ -667,8 +664,8 @@ where fn validate( &self, - deps: Option, - _env: Option<&Env>, + deps: Deps, + _env: &Env, info: Option<&MessageInfo>, _current: Option<&CollectionInfoAndExtensionResponse>, ) -> Result<(), Cw721ContractError> { @@ -679,7 +676,6 @@ where if self.symbol.is_some() && self.symbol.clone().unwrap().is_empty() { return Err(Cw721ContractError::CollectionSymbolEmpty {}); } - let deps = deps.ok_or(Cw721ContractError::NoDeps)?; // collection metadata can only be updated by the creator. creator assertion is skipped for these cases: // - CREATOR store is empty/not initioized (like in instantiation) // - info is none (like in migration) @@ -876,8 +872,8 @@ where { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, optional_current: Option<&NftInfo>, ) -> Result, Cw721ContractError> { @@ -898,7 +894,6 @@ where // None: create new NFT, note: msg is of same type, so we can clone it None => { let extension = self.extension.create(deps, env, info, None)?; - let deps = deps.ok_or(Cw721ContractError::NoDeps)?; let token_uri = empty_as_none(self.token_uri.clone()); Ok(NftInfo { owner: deps.api.addr_validate(&self.owner)?, // only for creation we use owner, but not for update! @@ -912,12 +907,11 @@ where fn validate( &self, - deps: Option, - _env: Option<&Env>, + deps: Deps, + _env: &Env, info: Option<&MessageInfo>, current: Option<&NftInfo>, ) -> Result<(), Cw721ContractError> { - let deps = deps.ok_or(Cw721ContractError::NoDeps)?; let info = info.ok_or(Cw721ContractError::NoInfo)?; if current.is_none() { // current is none: only minter can create new NFT @@ -974,8 +968,8 @@ impl From for NftExtensionMsg { impl StateFactory for NftExtensionMsg { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&NftExtension>, ) -> Result { @@ -1032,8 +1026,8 @@ impl StateFactory for NftExtensionMsg { fn validate( &self, - deps: Option, - _env: Option<&Env>, + deps: Deps, + _env: &Env, info: Option<&MessageInfo>, current: Option<&NftExtension>, ) -> Result<(), Cw721ContractError> { @@ -1041,7 +1035,6 @@ impl StateFactory for NftExtensionMsg { // - creator and minter can create NFT metadata // - only creator can update NFT metadata if current.is_none() { - let deps = deps.ok_or(Cw721ContractError::NoDeps)?; let info = info.ok_or(Cw721ContractError::NoInfo)?; // current is none: minter and creator can create new NFT metadata let minter_check = assert_minter(deps.storage, &info.sender); @@ -1050,7 +1043,6 @@ impl StateFactory for NftExtensionMsg { return Err(Cw721ContractError::NotMinterOrCreator {}); } } else { - let deps = deps.ok_or(Cw721ContractError::NoDeps)?; let info = info.ok_or(Cw721ContractError::NoInfo)?; // current is some: only creator can update NFT metadata assert_creator(deps.storage, &info.sender)?; @@ -1088,8 +1080,8 @@ where { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&Option>, ) -> Result, Cw721ContractError> { @@ -1106,8 +1098,8 @@ where fn validate( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&Option>, ) -> Result<(), Cw721ContractError> { diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index 8a081be4b..ef8ad50d1 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -360,8 +360,8 @@ pub struct Trait { impl StateFactory for Trait { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&Trait>, ) -> Result { @@ -371,8 +371,8 @@ impl StateFactory for Trait { fn validate( &self, - _deps: Option, - _env: Option<&Env>, + _deps: Deps, + _env: &Env, _info: Option<&MessageInfo>, _current: Option<&Trait>, ) -> Result<(), Cw721ContractError> { @@ -394,8 +394,8 @@ impl StateFactory for Trait { impl StateFactory> for Vec { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&Vec>, ) -> Result, Cw721ContractError> { @@ -405,8 +405,8 @@ impl StateFactory> for Vec { fn validate( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, _current: Option<&Vec>, ) -> Result<(), Cw721ContractError> { diff --git a/packages/cw721/src/traits.rs b/packages/cw721/src/traits.rs index 87f98e8af..0ff3d2ef1 100644 --- a/packages/cw721/src/traits.rs +++ b/packages/cw721/src/traits.rs @@ -67,15 +67,15 @@ pub trait Contains { pub trait StateFactory { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&TState>, ) -> Result; fn validate( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&TState>, ) -> Result<(), Cw721ContractError>; @@ -84,8 +84,8 @@ pub trait StateFactory { impl StateFactory for Empty { fn create( &self, - _deps: Option, - _env: Option<&Env>, + _deps: Deps, + _env: &Env, _info: Option<&MessageInfo>, _current: Option<&Empty>, ) -> Result { @@ -94,8 +94,8 @@ impl StateFactory for Empty { fn validate( &self, - _deps: Option, - _env: Option<&Env>, + _deps: Deps, + _env: &Env, _info: Option<&MessageInfo>, _current: Option<&Empty>, ) -> Result<(), Cw721ContractError> { @@ -188,7 +188,7 @@ pub trait Cw721Execute< ) -> Result, Cw721ContractError> { match msg { Cw721ExecuteMsg::UpdateCollectionInfo { collection_info } => { - self.update_collection_info(deps, info.into(), env.into(), collection_info) + self.update_collection_info(deps, info.into(), env, collection_info) } Cw721ExecuteMsg::Mint { token_id, @@ -365,7 +365,7 @@ pub trait Cw721Execute< &self, deps: DepsMut, info: Option<&MessageInfo>, - env: Option<&Env>, + env: &Env, msg: CollectionInfoMsg, ) -> Result, Cw721ContractError> { update_collection_info::( @@ -435,7 +435,7 @@ pub trait Cw721Execute< ) -> Result, Cw721ContractError> { update_nft_info::( deps, - env.into(), + env, info.into(), token_id, token_uri, From d423d0a75be81c1bfa8f7826aea1a9f64f0f6dab Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 12:44:51 +0200 Subject: [PATCH 02/13] remove build script, since there is `cargo make optimize` --- build.sh | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100755 build.sh diff --git a/build.sh b/build.sh deleted file mode 100755 index 60aa93a03..000000000 --- a/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# Compiles and optimizes contracts - -set -o errexit -o nounset -o pipefail -command -v shellcheck >/dev/null && shellcheck "$0" - -cd "$(git rev-parse --show-toplevel)" -docker run --rm -v "$(pwd)":/code \ - --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \ - --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/optimizer:0.16.0 # https://hub.docker.com/r/cosmwasm/optimizer/tags -ls -al ./artifacts/*wasm From 6382bda91a696312a4220da5a5f5f6c667fb19b7 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 14:54:24 +0200 Subject: [PATCH 03/13] use bech32 for handling real addresses (e.g. stars1... instead of simple string) --- Cargo.lock | 13 +- Cargo.toml | 3 + packages/cw721/Cargo.toml | 3 + packages/cw721/src/execute.rs | 2 +- packages/cw721/src/testing/multi_tests.rs | 350 +++++++++++++++++----- 5 files changed, 290 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c2c62afa7..67e7caa32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "block-buffer" version = "0.9.0" @@ -145,7 +151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c1556156fdf892a55cced6115968b961eaaadd6f724a2c2cb7d1e168e32dd3" dependencies = [ "base64", - "bech32", + "bech32 0.9.1", "bnum", "cosmwasm-crypto", "cosmwasm-derive", @@ -228,7 +234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc392a5cb7e778e3f90adbf7faa43c4db7f35b6623224b08886d796718edb875" dependencies = [ "anyhow", - "bech32", + "bech32 0.9.1", "cosmwasm-std", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", @@ -513,6 +519,8 @@ dependencies = [ name = "cw721" version = "0.19.0" dependencies = [ + "anyhow", + "bech32 0.11.0", "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", @@ -529,6 +537,7 @@ dependencies = [ "cw721-metadata-onchain 0.16.0", "schemars", "serde", + "sha2 0.10.8", "thiserror", "url", ] diff --git a/Cargo.toml b/Cargo.toml index e1c27399c..0348044fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,8 @@ documentation = "https://docs.cosmwasm.com" rust-version = "1.78" [workspace.dependencies] +anyhow = "^1.0" +bech32 = "^0.11" cosmwasm-schema = "^1.5" cosmwasm-std = "^1.5" cw2 = "^1.1" @@ -30,6 +32,7 @@ cw-storage-plus = "^1.1" cw-utils = "^1.0" schemars = "^0.8" serde = { version = "^1.0", default-features = false, features = ["derive"] } +sha2 = "^0.10" thiserror = "^1.0" url = "^2.5" diff --git a/packages/cw721/Cargo.toml b/packages/cw721/Cargo.toml index 58b2f05da..7eb6ea38f 100644 --- a/packages/cw721/Cargo.toml +++ b/packages/cw721/Cargo.toml @@ -30,9 +30,12 @@ thiserror = { workspace = true } url = { workspace = true } [dev-dependencies] +anyhow = { workspace = true } +bech32 = { workspace = true } cw-multi-test = { workspace = true } cw721-base-015 = { workspace = true, features = ["library"] } cw721-base-016 = { workspace = true, features = ["library"] } cw721-metadata-onchain-016 = { workspace = true} cw721-base-017 = { workspace = true, features = ["library"] } cw721-base-018 = { workspace = true, features = ["library"] } +sha2 = { workspace = true } diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index a293c037f..a1ee90ab3 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -648,7 +648,7 @@ pub fn migrate_minter( Cw721MigrateMsg::WithUpdate { minter, .. } => { if let Some(minter) = minter { MINTER.initialize_owner(storage, api, Some(minter.as_str()))?; - return Ok(response.add_attribute("creator", minter)); + return Ok(response.add_attribute("minter", minter)); } } } diff --git a/packages/cw721/src/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index 24bf333eb..446d27fb8 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -1,3 +1,5 @@ +use std::f32::MIN; + use crate::{ error::Cw721ContractError, extension::Cw721OnchainExtensions, @@ -10,21 +12,204 @@ use crate::{ DefaultOptionalCollectionExtension, DefaultOptionalCollectionExtensionMsg, DefaultOptionalNftExtension, DefaultOptionalNftExtensionMsg, NftExtensionMsg, }; +use anyhow::Result; +use bech32::{decode, encode, Hrp}; use cosmwasm_std::{ - to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, QuerierWrapper, Response, - WasmMsg, + instantiate2_address, to_json_binary, Addr, Api, Binary, CanonicalAddr, Deps, DepsMut, Empty, + Env, GovMsg, MemoryStorage, MessageInfo, QuerierWrapper, RecoverPubkeyError, Response, + StdError, StdResult, Storage, VerificationError, WasmMsg, }; use cw721_016::NftInfoResponse; -use cw_multi_test::{App, Contract, ContractWrapper, Executor}; +use cw_multi_test::{ + AddressGenerator, App, AppBuilder, BankKeeper, Contract, ContractWrapper, DistributionKeeper, + Executor, FailingModule, IbcAcceptingModule, Router, StakeKeeper, StargateFailing, WasmKeeper, +}; use cw_ownable::{Ownership, OwnershipError}; use cw_utils::Expiration; +use sha2::{digest::Update, Digest, Sha256}; use url::ParseError; +const BECH32_PREFIX_HRP: &str = "stars"; +pub const ADMIN_ADDR: &str = "admin"; 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"; +type MockRouter = Router< + BankKeeper, + FailingModule, + WasmKeeper, + StakeKeeper, + DistributionKeeper, + IbcAcceptingModule, + FailingModule, + StargateFailing, +>; + +type MockApp = App< + BankKeeper, + MockApiBech32, + MemoryStorage, + FailingModule, + WasmKeeper, + StakeKeeper, + DistributionKeeper, + IbcAcceptingModule, +>; + +#[derive(Default)] +pub struct MockAddressGenerator; + +impl AddressGenerator for MockAddressGenerator { + fn contract_address( + &self, + api: &dyn Api, + _storage: &mut dyn Storage, + code_id: u64, + instance_id: u64, + ) -> Result { + let canonical_addr = Self::instantiate_address(code_id, instance_id); + Ok(Addr::unchecked(api.addr_humanize(&canonical_addr)?)) + } + + fn predictable_contract_address( + &self, + api: &dyn Api, + _storage: &mut dyn Storage, + _code_id: u64, + _instance_id: u64, + checksum: &[u8], + creator: &CanonicalAddr, + salt: &[u8], + ) -> Result { + let canonical_addr = instantiate2_address(checksum, creator, salt)?; + Ok(Addr::unchecked(api.addr_humanize(&canonical_addr)?)) + } +} + +impl MockAddressGenerator { + // non-predictable contract address generator, see `BuildContractAddressClassic` + // implementation in wasmd: https://github.com/CosmWasm/wasmd/blob/main/x/wasm/keeper/addresses.go#L35-L42 + fn instantiate_address(code_id: u64, instance_id: u64) -> CanonicalAddr { + let mut key = Vec::::new(); + key.extend_from_slice(b"wasm\0"); + key.extend_from_slice(&code_id.to_be_bytes()); + key.extend_from_slice(&instance_id.to_be_bytes()); + let module = Sha256::digest("module".as_bytes()); + Sha256::new() + .chain(module) + .chain(key) + .finalize() + .to_vec() + .into() + } +} +pub struct MockApiBech32 { + prefix: Hrp, +} + +impl MockApiBech32 { + pub fn new(prefix: &'static str) -> Self { + Self { + prefix: Hrp::parse(prefix).unwrap(), + } + } +} + +impl Api for MockApiBech32 { + fn addr_validate(&self, input: &str) -> StdResult { + let canonical = self.addr_canonicalize(input)?; + let normalized = self.addr_humanize(&canonical)?; + if input != normalized { + Err(StdError::generic_err( + "Invalid input: address not normalized", + )) + } else { + Ok(Addr::unchecked(input)) + } + } + + fn addr_canonicalize(&self, input: &str) -> StdResult { + if let Ok((prefix, decoded)) = decode(input) { + if prefix == self.prefix { + return Ok(decoded.into()); + } + } + Err(StdError::generic_err(format!("Invalid input: {input}"))) + } + + fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult { + let hrp = self.prefix; + let data = canonical.as_slice(); + if let Ok(encoded) = encode::(hrp, data) { + Ok(Addr::unchecked(encoded)) + } else { + Err(StdError::generic_err("Invalid canonical address")) + } + } + + fn secp256k1_verify( + &self, + _message_hash: &[u8], + _signature: &[u8], + _public_key: &[u8], + ) -> Result { + unimplemented!() + } + + fn secp256k1_recover_pubkey( + &self, + _message_hash: &[u8], + _signature: &[u8], + _recovery_param: u8, + ) -> Result, RecoverPubkeyError> { + unimplemented!() + } + + fn ed25519_verify( + &self, + _message: &[u8], + _signature: &[u8], + _public_key: &[u8], + ) -> Result { + unimplemented!() + } + + fn ed25519_batch_verify( + &self, + _messages: &[&[u8]], + _signatures: &[&[u8]], + _public_keys: &[&[u8]], + ) -> Result { + unimplemented!() + } + + fn debug(&self, _message: &str) { + unimplemented!() + } +} + +impl MockApiBech32 { + pub fn addr_make(&self, input: &str) -> Addr { + let digest = Sha256::digest(input).to_vec(); + match encode::(self.prefix, &digest) { + Ok(address) => Addr::unchecked(address), + Err(reason) => panic!("Generating address failed with reason: {reason}"), + } + } +} + +fn new() -> MockApp { + AppBuilder::new() + .with_wasm::>( + WasmKeeper::new().with_address_generator(MockAddressGenerator), + ) + .with_ibc(IbcAcceptingModule::default()) + .with_api(MockApiBech32::new(BECH32_PREFIX_HRP)) + .build(no_init) +} + pub fn instantiate( deps: DepsMut, env: Env, @@ -67,6 +252,8 @@ pub fn migrate( contract.migrate(deps, env, msg, "contract_name", "contract_version") } +fn no_init(_router: &mut MockRouter, _api: &dyn Api, _storage: &mut dyn Storage) {} + fn cw721_base_latest_contract() -> Box> { let contract = ContractWrapper::new(execute, instantiate, query).with_migrate(migrate); Box::new(contract) @@ -148,7 +335,7 @@ fn query_nft_info( .unwrap() } -fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: String) { +fn mint_transfer_and_burn(app: &mut MockApp, cw721: Addr, sender: Addr, token_id: String) { app.execute_contract( sender.clone(), cw721.clone(), @@ -165,11 +352,12 @@ fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: St let owner = query_owner(app.wrap(), &cw721, token_id.clone()); assert_eq!(owner, sender.to_string()); + let burner = app.api().addr_make("burner"); app.execute_contract( sender, cw721.clone(), &Cw721ExecuteMsg::::TransferNft { - recipient: "burner".to_string(), + recipient: burner.to_string(), token_id: token_id.clone(), }, &[], @@ -177,10 +365,10 @@ fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: St .unwrap(); let owner = query_owner(app.wrap(), &cw721, token_id.clone()); - assert_eq!(owner, "burner".to_string()); + assert_eq!(owner, burner.to_string()); app.execute_contract( - Addr::unchecked("burner"), + burner, cw721, &Cw721ExecuteMsg::::Burn { token_id }, &[], @@ -191,10 +379,12 @@ fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: St #[test] fn test_operator() { // --- setup --- - let mut app = App::default(); - let admin = Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); + let creator = app.api().addr_make(CREATOR_ADDR); + let minter = app.api().addr_make(MINTER_ADDR); let code_id = app.store_code(cw721_base_latest_contract()); - let other = Addr::unchecked(OTHER_ADDR); + let other = app.api().addr_make(OTHER_ADDR); let cw721 = app .instantiate_contract( code_id, @@ -202,8 +392,8 @@ fn test_operator() { &Cw721InstantiateMsg:: { name: "collection".to_string(), symbol: "symbol".to_string(), - minter: Some(MINTER_ADDR.to_string()), - creator: Some(CREATOR_ADDR.to_string()), + minter: Some(minter.to_string()), + creator: Some(creator.to_string()), collection_info_extension: None, withdraw_address: None, }, @@ -213,8 +403,7 @@ fn test_operator() { ) .unwrap(); // mint - let minter = Addr::unchecked(MINTER_ADDR); - let nft_owner = Addr::unchecked(NFT_OWNER_ADDR); + let nft_owner = app.api().addr_make(NFT_OWNER_ADDR); app.execute_contract( minter, cw721.clone(), @@ -388,13 +577,13 @@ fn test_migration_legacy_to_latest() { // v0.15 migration by using existing minter addr { use cw721_base_015 as v15; - let mut app = App::default(); - let admin = Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); let code_id_016 = app.store_code(cw721_base_015_contract()); let code_id_latest = app.store_code(cw721_base_latest_contract()); - let legacy_creator_and_minter = Addr::unchecked("legacy_creator_and_minter"); + let legacy_creator_and_minter = app.api().addr_make("legacy_creator_and_minter"); let cw721 = app .instantiate_contract( @@ -526,13 +715,13 @@ fn test_migration_legacy_to_latest() { // v0.15 migration by providing new creator and minter addr { use cw721_base_015 as v15; - let mut app = App::default(); - let admin = Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); let code_id_015 = app.store_code(cw721_base_015_contract()); let code_id_latest = app.store_code(cw721_base_latest_contract()); - let legacy_creator_and_minter = Addr::unchecked("legacy_creator_and_minter"); + let legacy_creator_and_minter = app.api().addr_make("legacy_creator_and_minter"); let cw721 = app .instantiate_contract( @@ -557,20 +746,23 @@ fn test_migration_legacy_to_latest() { ); // migrate - app.execute( - admin, - WasmMsg::Migrate { - contract_addr: cw721.to_string(), - new_code_id: code_id_latest, - msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { - minter: Some(MINTER_ADDR.to_string()), - creator: Some(CREATOR_ADDR.to_string()), - }) - .unwrap(), - } - .into(), - ) - .unwrap(); + let creator = app.api().addr_make(CREATOR_ADDR); + let minter = app.api().addr_make(MINTER_ADDR); + let res = app + .execute( + admin, + WasmMsg::Migrate { + contract_addr: cw721.to_string(), + new_code_id: code_id_latest, + msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { + minter: Some(minter.to_string()), + creator: Some(creator.to_string()), + }) + .unwrap(), + } + .into(), + ) + .unwrap(); // legacy minter user cant mint let err: Cw721ContractError = app @@ -591,7 +783,6 @@ fn test_migration_legacy_to_latest() { assert_eq!(err, Cw721ContractError::NotMinter {}); // 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. @@ -640,7 +831,7 @@ fn test_migration_legacy_to_latest() { assert_eq!(minter_ownership.owner, Some(minter)); // check creator ownership query works - let creator = Addr::unchecked(CREATOR_ADDR); + let creator = app.api().addr_make(CREATOR_ADDR); let creator_ownership: Ownership = app .wrap() .query_wasm_smart( @@ -657,13 +848,13 @@ fn test_migration_legacy_to_latest() { // v0.16 migration by using existing minter addr { use cw721_base_016 as v16; - let mut app = App::default(); - let admin = Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); 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 legacy_creator_and_minter = app.api().addr_make("legacy_creator_and_minter"); let cw721 = app .instantiate_contract( @@ -795,13 +986,13 @@ fn test_migration_legacy_to_latest() { // v0.16 migration by providing new creator and minter addr { use cw721_base_016 as v16; - let mut app = App::default(); - let admin = Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); 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 legacy_creator_and_minter = app.api().addr_make("legacy_creator_and_minter"); let cw721 = app .instantiate_contract( @@ -826,14 +1017,16 @@ fn test_migration_legacy_to_latest() { ); // migrate + let creator = app.api().addr_make(CREATOR_ADDR); + let minter = app.api().addr_make(MINTER_ADDR); app.execute( admin, WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { - minter: Some(MINTER_ADDR.to_string()), - creator: Some(CREATOR_ADDR.to_string()), + minter: Some(minter.to_string()), + creator: Some(creator.to_string()), }) .unwrap(), } @@ -860,7 +1053,6 @@ fn test_migration_legacy_to_latest() { assert_eq!(err, Cw721ContractError::NotMinter {}); // 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. @@ -909,7 +1101,7 @@ fn test_migration_legacy_to_latest() { assert_eq!(minter_ownership.owner, Some(minter)); // check creator ownership query works - let creator = Addr::unchecked(CREATOR_ADDR); + let creator = app.api().addr_make(CREATOR_ADDR); let creator_ownership: Ownership = app .wrap() .query_wasm_smart( @@ -926,13 +1118,13 @@ fn test_migration_legacy_to_latest() { // v0.17 migration by using existing minter addr { use cw721_base_017 as v17; - let mut app = App::default(); - let admin = Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); 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 legacy_creator_and_minter = app.api().addr_make("legacy_creator_and_minter"); let cw721 = app .instantiate_contract( @@ -1064,13 +1256,13 @@ fn test_migration_legacy_to_latest() { // v0.17 migration by providing new creator and minter addr { use cw721_base_017 as v17; - let mut app = App::default(); - let admin = Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); 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 legacy_creator_and_minter = app.api().addr_make("legacy_creator_and_minter"); let cw721 = app .instantiate_contract( @@ -1095,14 +1287,16 @@ fn test_migration_legacy_to_latest() { ); // migrate + let creator = app.api().addr_make(CREATOR_ADDR); + let minter = app.api().addr_make(MINTER_ADDR); app.execute( admin, WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { - minter: Some(MINTER_ADDR.to_string()), - creator: Some(CREATOR_ADDR.to_string()), + minter: Some(minter.to_string()), + creator: Some(creator.to_string()), }) .unwrap(), } @@ -1129,7 +1323,6 @@ fn test_migration_legacy_to_latest() { assert_eq!(err, Cw721ContractError::NotMinter {}); // 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. @@ -1178,7 +1371,7 @@ fn test_migration_legacy_to_latest() { assert_eq!(minter_ownership.owner, Some(minter)); // check creator ownership query works - let creator = Addr::unchecked(CREATOR_ADDR); + let creator = app.api().addr_make(CREATOR_ADDR); let creator_ownership: Ownership = app .wrap() .query_wasm_smart( @@ -1195,13 +1388,13 @@ fn test_migration_legacy_to_latest() { // v0.18 migration by using existing minter addr { use cw721_base_018 as v18; - let mut app = App::default(); - let admin = Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); 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 legacy_creator_and_minter = app.api().addr_make("legacy_creator_and_minter"); let cw721 = app .instantiate_contract( @@ -1333,13 +1526,13 @@ fn test_migration_legacy_to_latest() { // v0.18 migration by providing new creator and minter addr { use cw721_base_018 as v18; - let mut app = App::default(); - let admin = Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); 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 legacy_creator_and_minter = app.api().addr_make("legacy_creator_and_minter"); let cw721 = app .instantiate_contract( @@ -1364,14 +1557,16 @@ fn test_migration_legacy_to_latest() { ); // migrate + let creator = app.api().addr_make(CREATOR_ADDR); + let minter = app.api().addr_make(MINTER_ADDR); app.execute( admin, WasmMsg::Migrate { contract_addr: cw721.to_string(), new_code_id: code_id_latest, msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { - minter: Some(MINTER_ADDR.to_string()), - creator: Some(CREATOR_ADDR.to_string()), + minter: Some(minter.to_string()), + creator: Some(creator.to_string()), }) .unwrap(), } @@ -1398,7 +1593,6 @@ fn test_migration_legacy_to_latest() { assert_eq!(err, Cw721ContractError::NotMinter {}); // 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. @@ -1447,7 +1641,7 @@ fn test_migration_legacy_to_latest() { assert_eq!(minter_ownership.owner, Some(minter)); // check creator ownership query works - let creator = Addr::unchecked(CREATOR_ADDR); + let creator = app.api().addr_make(CREATOR_ADDR); let creator_ownership: Ownership = app .wrap() .query_wasm_smart( @@ -1468,23 +1662,23 @@ fn test_migration_legacy_to_latest() { #[test] fn test_instantiate_016_msg() { use cw721_base_016 as v16; - let mut app = App::default(); - let admin = || Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); let code_id_latest = app.store_code(cw721_base_latest_contract()); let cw721 = app .instantiate_contract( code_id_latest, - admin(), + admin.clone(), &v16::InstantiateMsg { name: "collection".to_string(), symbol: "symbol".to_string(), - minter: admin().into_string(), + minter: admin.to_string(), }, &[], "cw721-base", - Some(admin().into_string()), + Some(admin.to_string()), ) .unwrap(); @@ -1506,10 +1700,11 @@ fn test_instantiate_016_msg() { #[test] fn test_update_nft_metadata() { // --- setup --- - let mut app = App::default(); - let admin = Addr::unchecked("admin"); + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); let code_id = app.store_code(cw721_base_latest_contract()); - let creator = Addr::unchecked(CREATOR_ADDR); + let creator = app.api().addr_make(CREATOR_ADDR); + let minter_addr = app.api().addr_make(MINTER_ADDR); let cw721 = app .instantiate_contract( code_id, @@ -1517,7 +1712,7 @@ fn test_update_nft_metadata() { &Cw721InstantiateMsg:: { name: "collection".to_string(), symbol: "symbol".to_string(), - minter: Some(MINTER_ADDR.to_string()), + minter: Some(minter_addr.to_string()), creator: None, // in case of none, sender is creator collection_info_extension: None, withdraw_address: None, @@ -1528,8 +1723,7 @@ fn test_update_nft_metadata() { ) .unwrap(); // mint - let minter = Addr::unchecked(MINTER_ADDR); - let nft_owner = Addr::unchecked(NFT_OWNER_ADDR); + let nft_owner = app.api().addr_make(NFT_OWNER_ADDR); let nft_metadata_msg = NftExtensionMsg { image: Some("ipfs://foo.bar/image.png".to_string()), image_data: Some("image data".to_string()), @@ -1546,7 +1740,7 @@ fn test_update_nft_metadata() { youtube_url: Some("file://youtube_url".to_string()), }; app.execute_contract( - minter, + minter_addr, cw721.clone(), &Cw721ExecuteMsg::< DefaultOptionalNftExtensionMsg, From 368c881d24f5fbf093bf0cca7cc2f32c30fad5ac Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 16:16:15 +0200 Subject: [PATCH 04/13] clippy --- packages/cw721/src/testing/multi_tests.rs | 31 ++++++++++------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/cw721/src/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index 446d27fb8..102d7e2d5 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -1,5 +1,3 @@ -use std::f32::MIN; - use crate::{ error::Cw721ContractError, extension::Cw721OnchainExtensions, @@ -748,21 +746,20 @@ fn test_migration_legacy_to_latest() { // migrate let creator = app.api().addr_make(CREATOR_ADDR); let minter = app.api().addr_make(MINTER_ADDR); - let res = app - .execute( - admin, - WasmMsg::Migrate { - contract_addr: cw721.to_string(), - new_code_id: code_id_latest, - msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { - minter: Some(minter.to_string()), - creator: Some(creator.to_string()), - }) - .unwrap(), - } - .into(), - ) - .unwrap(); + app.execute( + admin, + WasmMsg::Migrate { + contract_addr: cw721.to_string(), + new_code_id: code_id_latest, + msg: to_json_binary(&Cw721MigrateMsg::WithUpdate { + minter: Some(minter.to_string()), + creator: Some(creator.to_string()), + }) + .unwrap(), + } + .into(), + ) + .unwrap(); // legacy minter user cant mint let err: Cw721ContractError = app From 5557e7985186877d04e60992ccdf43eaacfc0f88 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 16:50:06 +0200 Subject: [PATCH 05/13] validate addr --- packages/cw721/src/msg.rs | 13 +- packages/cw721/src/testing/multi_tests.rs | 146 +++++++++++++++------- packages/cw721/src/testing/unit_tests.rs | 6 +- 3 files changed, 113 insertions(+), 52 deletions(-) diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index 1f368e703..fb7ba6382 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -532,14 +532,14 @@ impl StateFactory for RoyaltyInfoResponse { // Some: update existing royalty info Some(current) => { let mut updated = current.clone(); - updated.payment_address = deps.api.addr_validate(self.payment_address.as_str())?; + updated.payment_address = Addr::unchecked(self.payment_address.as_str()); // no check needed, since it is already done in validate updated.share = self.share; Ok(updated) } // None: create new royalty info None => { let new = RoyaltyInfo { - payment_address: deps.api.addr_validate(self.payment_address.as_str())?, + payment_address: Addr::unchecked(self.payment_address.as_str()), // no check needed, since it is already done in validate share: self.share, }; Ok(new) @@ -549,11 +549,12 @@ impl StateFactory for RoyaltyInfoResponse { fn validate( &self, - _deps: Deps, + deps: Deps, _env: &Env, _info: Option<&MessageInfo>, current: Option<&RoyaltyInfo>, ) -> Result<(), Cw721ContractError> { + println!(">>>> RoyaltyInfoResponse.validate: {:?}", self); if let Some(current_royalty_info) = current { // check max share delta if current_royalty_info.share < self.share { @@ -572,6 +573,8 @@ impl StateFactory for RoyaltyInfoResponse { "Share cannot be greater than {MAX_ROYALTY_SHARE_PCT}%" ))); } + // validate payment address + deps.api.addr_validate(self.payment_address.as_str())?; Ok(()) } } @@ -896,7 +899,7 @@ where let extension = self.extension.create(deps, env, info, None)?; let token_uri = empty_as_none(self.token_uri.clone()); Ok(NftInfo { - owner: deps.api.addr_validate(&self.owner)?, // only for creation we use owner, but not for update! + owner: Addr::unchecked(&self.owner), // only for creation we use owner, but not for update! approvals: vec![], token_uri, extension, @@ -925,6 +928,8 @@ where if let Some(token_uri) = token_uri { Url::parse(token_uri.as_str())?; } + // validate owner + deps.api.addr_validate(&self.owner)?; Ok(()) } } diff --git a/packages/cw721/src/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index 102d7e2d5..2c5e6a280 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -2,8 +2,8 @@ use crate::{ error::Cw721ContractError, extension::Cw721OnchainExtensions, msg::{ - Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, Cw721QueryMsg, MinterResponse, - NumTokensResponse, OwnerOfResponse, + CollectionExtensionMsg, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, + Cw721QueryMsg, MinterResponse, NumTokensResponse, OwnerOfResponse, RoyaltyInfoResponse, }, state::{NftExtension, Trait}, traits::{Cw721Execute, Cw721Query}, @@ -13,9 +13,9 @@ use crate::{ use anyhow::Result; use bech32::{decode, encode, Hrp}; use cosmwasm_std::{ - instantiate2_address, to_json_binary, Addr, Api, Binary, CanonicalAddr, Deps, DepsMut, Empty, - Env, GovMsg, MemoryStorage, MessageInfo, QuerierWrapper, RecoverPubkeyError, Response, - StdError, StdResult, Storage, VerificationError, WasmMsg, + instantiate2_address, to_json_binary, Addr, Api, Binary, CanonicalAddr, Decimal, Deps, DepsMut, + Empty, Env, GovMsg, MemoryStorage, MessageInfo, QuerierWrapper, RecoverPubkeyError, Response, + StdError, StdResult, Storage, Timestamp, VerificationError, WasmMsg, }; use cw721_016::NftInfoResponse; use cw_multi_test::{ @@ -31,7 +31,8 @@ const BECH32_PREFIX_HRP: &str = "stars"; pub const ADMIN_ADDR: &str = "admin"; pub const CREATOR_ADDR: &str = "creator"; pub const MINTER_ADDR: &str = "minter"; -pub const OTHER_ADDR: &str = "other"; +pub const OTHER1_ADDR: &str = "other"; +pub const OTHER2_ADDR: &str = "other"; pub const NFT_OWNER_ADDR: &str = "nft_owner"; type MockRouter = Router< @@ -382,7 +383,7 @@ fn test_operator() { let creator = app.api().addr_make(CREATOR_ADDR); let minter = app.api().addr_make(MINTER_ADDR); let code_id = app.store_code(cw721_base_latest_contract()); - let other = app.api().addr_make(OTHER_ADDR); + let other = app.api().addr_make(OTHER1_ADDR); let cw721 = app .instantiate_contract( code_id, @@ -622,7 +623,7 @@ fn test_migration_legacy_to_latest() { .unwrap(); // non-minter user cant mint - let other = Addr::unchecked(OTHER_ADDR); + let other = Addr::unchecked(OTHER1_ADDR); let err: Cw721ContractError = app .execute_contract( other.clone(), @@ -892,7 +893,7 @@ fn test_migration_legacy_to_latest() { .unwrap(); // non-minter user cant mint - let other = Addr::unchecked(OTHER_ADDR); + let other = Addr::unchecked(OTHER1_ADDR); let err: Cw721ContractError = app .execute_contract( other.clone(), @@ -1162,7 +1163,7 @@ fn test_migration_legacy_to_latest() { .unwrap(); // non-minter user cant mint - let other = Addr::unchecked(OTHER_ADDR); + let other = Addr::unchecked(OTHER1_ADDR); let err: Cw721ContractError = app .execute_contract( other.clone(), @@ -1432,7 +1433,7 @@ fn test_migration_legacy_to_latest() { .unwrap(); // non-minter user cant mint - let other = Addr::unchecked(OTHER_ADDR); + let other = Addr::unchecked(OTHER1_ADDR); let err: Cw721ContractError = app .execute_contract( other.clone(), @@ -1654,44 +1655,99 @@ fn test_migration_legacy_to_latest() { } } -/// Test backward compatibility using instantiate msg from a 0.16 version on latest contract. -/// This ensures existing 3rd party contracts doesnt need to update as well. #[test] -fn test_instantiate_016_msg() { - use cw721_base_016 as v16; - let mut app = new(); - let admin = app.api().addr_make(ADMIN_ADDR); +fn test_instantiate() { + // test case: happy path + { + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); + let minter = app.api().addr_make(MINTER_ADDR); + let creator = app.api().addr_make(CREATOR_ADDR); + let payment_address = app.api().addr_make(OTHER1_ADDR); + let withdraw_addr = app.api().addr_make(OTHER2_ADDR); - let code_id_latest = app.store_code(cw721_base_latest_contract()); + let code_id_latest = app.store_code(cw721_base_latest_contract()); - let cw721 = app - .instantiate_contract( - code_id_latest, - admin.clone(), - &v16::InstantiateMsg { - name: "collection".to_string(), - symbol: "symbol".to_string(), - minter: admin.to_string(), - }, - &[], - "cw721-base", - Some(admin.to_string()), - ) - .unwrap(); + let cw721 = app + .instantiate_contract( + code_id_latest, + admin.clone(), + &Cw721InstantiateMsg { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: Some(minter.to_string()), + creator: Some(creator.to_string()), + withdraw_address: Some(withdraw_addr.to_string()), + collection_info_extension: Some(CollectionExtensionMsg { + description: Some("description".to_string()), + image: Some("ipfs://ark.pass".to_string()), + explicit_content: Some(false), + external_link: Some("https://interchain.arkprotocol.io".to_string()), + start_trading_time: Some(Timestamp::from_seconds(42)), + royalty_info: Some(RoyaltyInfoResponse { + payment_address: payment_address.to_string(), + share: Decimal::bps(1000), + }), + }), + }, + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); - // assert withdraw address is None - let withdraw_addr: Option = app - .wrap() - .query_wasm_smart( - cw721, - &Cw721QueryMsg::< - DefaultOptionalNftExtension, - DefaultOptionalCollectionExtension, - Empty, - >::GetWithdrawAddress {}, - ) - .unwrap(); - assert!(withdraw_addr.is_none()); + // assert withdraw address + let withdraw_addr_result: Option = app + .wrap() + .query_wasm_smart( + cw721, + &Cw721QueryMsg::< + DefaultOptionalNftExtension, + DefaultOptionalCollectionExtension, + Empty, + >::GetWithdrawAddress {}, + ) + .unwrap(); + assert_eq!(withdraw_addr_result, Some(withdraw_addr.to_string())); + } + // test case: backward compatibility using instantiate msg from a 0.16 version on latest contract. + // This ensures existing 3rd party contracts doesnt need to update as well. + { + use cw721_base_016 as v16; + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); + + let code_id_latest = app.store_code(cw721_base_latest_contract()); + + let cw721 = app + .instantiate_contract( + code_id_latest, + admin.clone(), + &v16::InstantiateMsg { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: admin.to_string(), + }, + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); + + // assert withdraw address is None + let withdraw_addr: Option = app + .wrap() + .query_wasm_smart( + cw721, + &Cw721QueryMsg::< + DefaultOptionalNftExtension, + DefaultOptionalCollectionExtension, + Empty, + >::GetWithdrawAddress {}, + ) + .unwrap(); + assert!(withdraw_addr.is_none()); + } } #[test] diff --git a/packages/cw721/src/testing/unit_tests.rs b/packages/cw721/src/testing/unit_tests.rs index b93510ecd..08fbf4374 100644 --- a/packages/cw721/src/testing/unit_tests.rs +++ b/packages/cw721/src/testing/unit_tests.rs @@ -20,7 +20,7 @@ use cosmwasm_std::{ use cw2::ContractVersion; use cw_ownable::Action; use cw_storage_plus::Item; -use unit_tests::multi_tests::{CREATOR_ADDR, MINTER_ADDR, OTHER_ADDR}; +use unit_tests::multi_tests::{CREATOR_ADDR, MINTER_ADDR, OTHER1_ADDR}; use super::*; @@ -146,7 +146,7 @@ fn test_instantiation_with_proper_minter_and_creator() { { let mut deps = mock_dependencies(); - let info = mock_info(OTHER_ADDR, &[]); + let info = mock_info(OTHER1_ADDR, &[]); Cw721OnchainExtensions::default() .instantiate_with_version( deps.as_mut(), @@ -982,7 +982,7 @@ fn test_collection_info_update() { symbol: Some("new_collection_symbol".into()), extension: Some(updated_extension_msg), }; - let info_other = mock_info(OTHER_ADDR, &[]); + let info_other = mock_info(OTHER1_ADDR, &[]); let err = contract .execute( deps.as_mut(), From bcd8a7cd341af32fa8cc3938bb3825b42855bd86 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 19:11:20 +0200 Subject: [PATCH 06/13] test invalid addresses --- packages/cw721/src/msg.rs | 1 - packages/cw721/src/testing/multi_tests.rs | 144 +++++++++++++++++----- 2 files changed, 115 insertions(+), 30 deletions(-) diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index fb7ba6382..02a3cc5d6 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -554,7 +554,6 @@ impl StateFactory for RoyaltyInfoResponse { _info: Option<&MessageInfo>, current: Option<&RoyaltyInfo>, ) -> Result<(), Cw721ContractError> { - println!(">>>> RoyaltyInfoResponse.validate: {:?}", self); if let Some(current_royalty_info) = current { // check max share delta if current_royalty_info.share < self.share { diff --git a/packages/cw721/src/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index 2c5e6a280..2463e7679 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -1657,39 +1657,38 @@ fn test_migration_legacy_to_latest() { #[test] fn test_instantiate() { + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); + let minter = app.api().addr_make(MINTER_ADDR); + let creator = app.api().addr_make(CREATOR_ADDR); + let payment_address = app.api().addr_make(OTHER1_ADDR); + let withdraw_addr = app.api().addr_make(OTHER2_ADDR); + let init_msg = Cw721InstantiateMsg { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: Some(minter.to_string()), + creator: Some(creator.to_string()), + withdraw_address: Some(withdraw_addr.to_string()), + collection_info_extension: Some(CollectionExtensionMsg { + description: Some("description".to_string()), + image: Some("ipfs://ark.pass".to_string()), + explicit_content: Some(false), + external_link: Some("https://interchain.arkprotocol.io".to_string()), + start_trading_time: Some(Timestamp::from_seconds(42)), + royalty_info: Some(RoyaltyInfoResponse { + payment_address: payment_address.to_string(), + share: Decimal::bps(1000), + }), + }), + }; // test case: happy path { - let mut app = new(); - let admin = app.api().addr_make(ADMIN_ADDR); - let minter = app.api().addr_make(MINTER_ADDR); - let creator = app.api().addr_make(CREATOR_ADDR); - let payment_address = app.api().addr_make(OTHER1_ADDR); - let withdraw_addr = app.api().addr_make(OTHER2_ADDR); - let code_id_latest = app.store_code(cw721_base_latest_contract()); - let cw721 = app .instantiate_contract( code_id_latest, admin.clone(), - &Cw721InstantiateMsg { - name: "collection".to_string(), - symbol: "symbol".to_string(), - minter: Some(minter.to_string()), - creator: Some(creator.to_string()), - withdraw_address: Some(withdraw_addr.to_string()), - collection_info_extension: Some(CollectionExtensionMsg { - description: Some("description".to_string()), - image: Some("ipfs://ark.pass".to_string()), - explicit_content: Some(false), - external_link: Some("https://interchain.arkprotocol.io".to_string()), - start_trading_time: Some(Timestamp::from_seconds(42)), - royalty_info: Some(RoyaltyInfoResponse { - payment_address: payment_address.to_string(), - share: Decimal::bps(1000), - }), - }), - }, + &init_msg.clone(), &[], "cw721-base", Some(admin.to_string()), @@ -1710,13 +1709,100 @@ fn test_instantiate() { .unwrap(); assert_eq!(withdraw_addr_result, Some(withdraw_addr.to_string())); } + // test case: invalid addresses + { + // invalid creator + let code_id_latest = app.store_code(cw721_base_latest_contract()); + let mut invalid_init_msg = init_msg.clone(); + invalid_init_msg.creator = Some("invalid".to_string()); + let error: Cw721ContractError = app + .instantiate_contract( + code_id_latest, + admin.clone(), + &invalid_init_msg.clone(), + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!( + error, + Cw721ContractError::Std(StdError::generic_err("Invalid input: invalid")) + ); + // invalid minter + let mut invalid_init_msg = init_msg.clone(); + invalid_init_msg.minter = Some("invalid".to_string()); + let error: Cw721ContractError = app + .instantiate_contract( + code_id_latest, + admin.clone(), + &invalid_init_msg.clone(), + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!( + error, + Cw721ContractError::Std(StdError::generic_err("Invalid input: invalid")) + ); + // invalid withdraw addr + let mut invalid_init_msg = init_msg.clone(); + invalid_init_msg.withdraw_address = Some("invalid".to_string()); + let error: Cw721ContractError = app + .instantiate_contract( + code_id_latest, + admin.clone(), + &invalid_init_msg.clone(), + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!( + error, + Cw721ContractError::Std(StdError::generic_err("Invalid input: invalid")) + ); + // invalid payment addr + let mut invalid_init_msg = init_msg.clone(); + invalid_init_msg.collection_info_extension = Some(CollectionExtensionMsg { + description: Some("description".to_string()), + image: Some("ipfs://ark.pass".to_string()), + explicit_content: Some(false), + external_link: Some("https://interchain.arkprotocol.io".to_string()), + start_trading_time: Some(Timestamp::from_seconds(42)), + royalty_info: Some(RoyaltyInfoResponse { + payment_address: "invalid".to_string(), + share: Decimal::bps(1000), + }), + }); + let error: Cw721ContractError = app + .instantiate_contract( + code_id_latest, + admin.clone(), + &invalid_init_msg.clone(), + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!( + error, + Cw721ContractError::Std(StdError::generic_err("Invalid input: invalid")) + ); + } // test case: backward compatibility using instantiate msg from a 0.16 version on latest contract. // This ensures existing 3rd party contracts doesnt need to update as well. { use cw721_base_016 as v16; - let mut app = new(); - let admin = app.api().addr_make(ADMIN_ADDR); - let code_id_latest = app.store_code(cw721_base_latest_contract()); let cw721 = app From 71d692b315f93c709323793a74710c7df0c42154 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 19:38:52 +0200 Subject: [PATCH 07/13] new GetConfig --- contracts/cw721-base/examples/schema.rs | 13 +- contracts/cw721-base/schema/cw721-base.json | 3315 +++++++++++++++++ contracts/cw721-base/schema/execute_msg.json | 663 ---- .../cw721-base/schema/instantiate_msg.json | 136 - contracts/cw721-base/schema/migrate_msg.json | 33 - contracts/cw721-base/schema/query_msg.json | 603 --- contracts/cw721-base/src/lib.rs | 44 +- .../cw721-metadata-onchain/examples/schema.rs | 14 +- .../schema/cw721-metadata-onchain.json | 293 ++ contracts/cw721-metadata-onchain/src/error.rs | 2 + contracts/cw721-metadata-onchain/src/lib.rs | 98 +- contracts/cw721-metadata-onchain/src/msg.rs | 20 + packages/cw721/examples/schema.rs | 7 +- .../cw721/schema/all_collection_info.json | 279 ++ packages/cw721/schema/cw721_query_msg.json | 14 + packages/cw721/src/msg.rs | 16 + packages/cw721/src/query.rs | 29 +- packages/cw721/src/state.rs | 12 +- packages/cw721/src/testing/multi_tests.rs | 162 +- packages/cw721/src/traits.rs | 18 +- 20 files changed, 4207 insertions(+), 1564 deletions(-) create mode 100644 contracts/cw721-base/schema/cw721-base.json delete mode 100644 contracts/cw721-base/schema/execute_msg.json delete mode 100644 contracts/cw721-base/schema/instantiate_msg.json delete mode 100644 contracts/cw721-base/schema/migrate_msg.json delete mode 100644 contracts/cw721-base/schema/query_msg.json create mode 100644 contracts/cw721-metadata-onchain/src/error.rs create mode 100644 contracts/cw721-metadata-onchain/src/msg.rs create mode 100644 packages/cw721/schema/all_collection_info.json diff --git a/contracts/cw721-base/examples/schema.rs b/contracts/cw721-base/examples/schema.rs index 72cde247a..d31c12dc7 100644 --- a/contracts/cw721-base/examples/schema.rs +++ b/contracts/cw721-base/examples/schema.rs @@ -1,4 +1,4 @@ -use cosmwasm_schema::{export_schema_with_title, remove_schemas, schema_for}; +use cosmwasm_schema::{remove_schemas, write_api}; use cw721_base::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; use std::env::current_dir; use std::fs::create_dir_all; @@ -9,9 +9,10 @@ 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!(ExecuteMsg), &out_dir, "ExecuteMsg"); - export_schema_with_title(&schema_for!(QueryMsg), &out_dir, "QueryMsg"); - export_schema_with_title(&schema_for!(MigrateMsg), &out_dir, "MigrateMsg"); + write_api! { + instantiate: InstantiateMsg, + execute: ExecuteMsg, + query: QueryMsg, + migrate: MigrateMsg, + } } diff --git a/contracts/cw721-base/schema/cw721-base.json b/contracts/cw721-base/schema/cw721-base.json new file mode 100644 index 000000000..f61e702db --- /dev/null +++ b/contracts/cw721-base/schema/cw721-base.json @@ -0,0 +1,3315 @@ +{ + "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": { + "description": "Optional extension of the collection metadata", + "anyOf": [ + { + "$ref": "#/definitions/CollectionExtensionMsg_for_RoyaltyInfoResponse" + }, + { + "type": "null" + } + ] + }, + "creator": { + "description": "Sets the creator of collection. The creator is the only one eligible to update `CollectionInfo`.", + "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": { + "CollectionExtensionMsg_for_RoyaltyInfoResponse": { + "description": "NOTE: In case `info` is not provided in `create()` or `validate()` (like for migration), creator/minter assertion is skipped.", + "type": "object", + "properties": { + "description": { + "type": [ + "string", + "null" + ] + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfoResponse" + }, + { + "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" + }, + "RoyaltyInfoResponse": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "type": "string" + }, + "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", + "oneOf": [ + { + "description": "Deprecated: use UpdateMinterOwnership instead! Will be removed in next release!", + "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": "The creator is the only one eligible to update `CollectionInfo`.", + "type": "object", + "required": [ + "update_collection_info" + ], + "properties": { + "update_collection_info": { + "type": "object", + "required": [ + "collection_info" + ], + "properties": { + "collection_info": { + "$ref": "#/definitions/CollectionInfoMsg_for_Nullable_CollectionExtensionMsg_for_RoyaltyInfoResponse" + } + }, + "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/Empty" + }, + { + "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": "Custom msg execution. This is a no-op in default implementation.", + "type": "object", + "required": [ + "update_extension" + ], + "properties": { + "update_extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "The creator is the only one eligible to update NFT's token uri and onchain metadata (`NftInfo.extension`). NOTE: approvals and owner are not affected by this call, since they belong to the NFT owner.", + "type": "object", + "required": [ + "update_nft_info" + ], + "properties": { + "update_nft_info": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, + "token_id": { + "type": "string" + }, + "token_uri": { + "description": "NOTE: Empty string is handled as None", + "type": [ + "string", + "null" + ] + } + }, + "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" + } + } + }, + "CollectionExtensionMsg_for_RoyaltyInfoResponse": { + "description": "NOTE: In case `info` is not provided in `create()` or `validate()` (like for migration), creator/minter assertion is skipped.", + "type": "object", + "properties": { + "description": { + "type": [ + "string", + "null" + ] + }, + "explicit_content": { + "type": [ + "boolean", + "null" + ] + }, + "external_link": { + "type": [ + "string", + "null" + ] + }, + "image": { + "type": [ + "string", + "null" + ] + }, + "royalty_info": { + "anyOf": [ + { + "$ref": "#/definitions/RoyaltyInfoResponse" + }, + { + "type": "null" + } + ] + }, + "start_trading_time": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "CollectionInfoMsg_for_Nullable_CollectionExtensionMsg_for_RoyaltyInfoResponse": { + "type": "object", + "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionExtensionMsg_for_RoyaltyInfoResponse" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "symbol": { + "type": [ + "string", + "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" + }, + "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 + } + ] + }, + "RoyaltyInfoResponse": { + "type": "object", + "required": [ + "payment_address", + "share" + ], + "properties": { + "payment_address": { + "type": "string" + }, + "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" + } + } + }, + "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 + }, + { + "description": "Deprecated: use GetCollectionInfoAndExtension instead! Will be removed in next release!", + "deprecated": true, + "type": "object", + "required": [ + "contract_info" + ], + "properties": { + "contract_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns `AllCollectionInfoResponse`", + "type": "object", + "required": [ + "get_config" + ], + "properties": { + "get_config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns `CollectionInfoAndExtensionResponse`", + "type": "object", + "required": [ + "get_collection_info_and_extension" + ], + "properties": { + "get_collection_info_and_extension": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "returns `AllInfoResponse` which contains contract, collection and nft details", + "type": "object", + "required": [ + "get_all_info" + ], + "properties": { + "get_all_info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns `CollectionExtensionAttributes`", + "type": "object", + "required": [ + "get_collection_extension_attributes" + ], + "properties": { + "get_collection_extension_attributes": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated: use GetMinterOwnership instead! Will be removed in next release!", + "deprecated": true, + "type": "object", + "required": [ + "ownership" + ], + "properties": { + "ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Return the minter Deprecated: use GetMinterOwnership instead! Will be removed in next release!", + "deprecated": true, + "type": "object", + "required": [ + "minter" + ], + "properties": { + "minter": { + "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 + }, + { + "type": "object", + "required": [ + "get_nft_by_extension" + ], + "properties": { + "get_nft_by_extension": { + "type": "object", + "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "type": "null" + } + ] + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "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" + ] + }, + "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": "Custom msg query. Default implementation returns an empty binary.", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_collection_extension" + ], + "properties": { + "get_collection_extension": { + "type": "object", + "properties": { + "msg": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionExtension_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" + }, + "CollectionExtension_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" + } + } + }, + "migrate": { + "$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 + } + ] + }, + "sudo": null, + "responses": { + "all_nft_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllNftInfoResponse_for_Nullable_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_Nullable_Empty" + } + ] + } + }, + "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 + }, + "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_Nullable_Empty": { + "type": "object", + "properties": { + "extension": { + "description": "You can add any custom metadata here when you extend cw721-base", + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "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" + } + ] + }, + "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": "CollectionInfoAndExtensionResponse_for_Nullable_CollectionExtension_for_RoyaltyInfo", + "description": "This is a wrapper around CollectionInfo that includes the extension.", + "type": "object", + "required": [ + "name", + "symbol", + "updated_at" + ], + "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionExtension_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" + }, + "CollectionExtension_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" + } + } + }, + "extension": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Null", + "type": "null" + }, + "get_all_info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllInfoResponse", + "description": "This is a wrapper around CollectionInfo that includes the extension, contract info, and number of tokens (supply).", + "type": "object", + "required": [ + "collection_extension", + "collection_info", + "contract_info", + "num_tokens" + ], + "properties": { + "collection_extension": { + "type": "array", + "items": { + "$ref": "#/definitions/Attribute" + } + }, + "collection_info": { + "$ref": "#/definitions/CollectionInfo" + }, + "contract_info": { + "$ref": "#/definitions/ContractInfoResponse" + }, + "num_tokens": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Attribute": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + }, + "additionalProperties": false + }, + "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" + }, + "CollectionInfo": { + "type": "object", + "required": [ + "name", + "symbol", + "updated_at" + ], + "properties": { + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + "ContractInfoResponse": { + "type": "object", + "required": [ + "code_id", + "creator", + "pinned" + ], + "properties": { + "admin": { + "description": "admin who can run migrations (if any)", + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "creator": { + "description": "address that instantiated this contract", + "type": "string" + }, + "ibc_port": { + "description": "set if this contract has bound an IBC port", + "type": [ + "string", + "null" + ] + }, + "pinned": { + "description": "if set, the contract is pinned to the cache, and thus uses less gas when called", + "type": "boolean" + } + } + }, + "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_extension": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Null", + "type": "null" + }, + "get_collection_extension_attributes": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_Attribute", + "type": "array", + "items": { + "$ref": "#/definitions/Attribute" + }, + "definitions": { + "Attribute": { + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + }, + "additionalProperties": false + }, + "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" + } + } + }, + "get_collection_info_and_extension": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollectionInfoAndExtensionResponse_for_Nullable_CollectionExtension_for_RoyaltyInfo", + "description": "This is a wrapper around CollectionInfo that includes the extension.", + "type": "object", + "required": [ + "name", + "symbol", + "updated_at" + ], + "properties": { + "extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionExtension_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" + }, + "CollectionExtension_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_config": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse_for_Nullable_CollectionExtension_for_RoyaltyInfo", + "description": "This is a wrapper around CollectionInfo that includes the extension.", + "type": "object", + "required": [ + "collection_info", + "contract_info", + "creator_ownership", + "minter_ownership", + "num_tokens" + ], + "properties": { + "collection_extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "collection_info": { + "$ref": "#/definitions/CollectionInfo" + }, + "contract_info": { + "$ref": "#/definitions/ContractInfoResponse" + }, + "creator_ownership": { + "$ref": "#/definitions/Ownership_for_Addr" + }, + "minter_ownership": { + "$ref": "#/definitions/Ownership_for_Addr" + }, + "num_tokens": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "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" + }, + "CollectionExtension_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 + }, + "CollectionInfo": { + "type": "object", + "required": [ + "name", + "symbol", + "updated_at" + ], + "properties": { + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + "ContractInfoResponse": { + "type": "object", + "required": [ + "code_id", + "creator", + "pinned" + ], + "properties": { + "admin": { + "description": "admin who can run migrations (if any)", + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "creator": { + "description": "address that instantiated this contract", + "type": "string" + }, + "ibc_port": { + "description": "set if this contract has bound an IBC port", + "type": [ + "string", + "null" + ] + }, + "pinned": { + "description": "if set, the contract is pinned to the cache, and thus uses less gas when called", + "type": "boolean" + } + } + }, + "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" + }, + "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 + } + ] + }, + "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 + }, + "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_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_nft_by_extension": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_NftInfoResponse_for_Nullable_Empty", + "anyOf": [ + { + "$ref": "#/definitions/NftInfoResponse_for_Nullable_Empty" + }, + { + "type": "null" + } + ], + "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" + }, + "NftInfoResponse_for_Nullable_Empty": { + "type": "object", + "properties": { + "extension": { + "description": "You can add any custom metadata here when you extend cw721-base", + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "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 + } + } + }, + "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_Empty", + "type": "object", + "properties": { + "extension": { + "description": "You can add any custom metadata here when you extend cw721-base", + "anyOf": [ + { + "$ref": "#/definitions/Empty" + }, + { + "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": { + "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": { + "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-base/schema/execute_msg.json b/contracts/cw721-base/schema/execute_msg.json deleted file mode 100644 index d464db611..000000000 --- a/contracts/cw721-base/schema/execute_msg.json +++ /dev/null @@ -1,663 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ExecuteMsg", - "oneOf": [ - { - "description": "Deprecated: use UpdateMinterOwnership instead! Will be removed in next release!", - "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": "The creator is the only one eligible to update `CollectionInfo`.", - "type": "object", - "required": [ - "update_collection_info" - ], - "properties": { - "update_collection_info": { - "type": "object", - "required": [ - "collection_info" - ], - "properties": { - "collection_info": { - "$ref": "#/definitions/CollectionInfoMsg_for_Nullable_CollectionExtensionMsg_for_RoyaltyInfoResponse" - } - }, - "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/Empty" - }, - { - "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": "Custom msg execution. This is a no-op in default implementation.", - "type": "object", - "required": [ - "update_extension" - ], - "properties": { - "update_extension": { - "type": "object", - "required": [ - "msg" - ], - "properties": { - "msg": { - "$ref": "#/definitions/Empty" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "The creator is the only one eligible to update NFT's token uri and onchain metadata (`NftInfo.extension`). NOTE: approvals and owner are not affected by this call, since they belong to the NFT owner.", - "type": "object", - "required": [ - "update_nft_info" - ], - "properties": { - "update_nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/Empty" - }, - { - "type": "null" - } - ] - }, - "token_id": { - "type": "string" - }, - "token_uri": { - "description": "NOTE: Empty string is handled as None", - "type": [ - "string", - "null" - ] - } - }, - "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" - } - } - }, - "CollectionExtensionMsg_for_RoyaltyInfoResponse": { - "description": "NOTE: In case `info` is not provided in `create()` or `validate()` (like for migration), creator/minter assertion is skipped.", - "type": "object", - "properties": { - "description": { - "type": [ - "string", - "null" - ] - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": [ - "string", - "null" - ] - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfoResponse" - }, - { - "type": "null" - } - ] - }, - "start_trading_time": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "CollectionInfoMsg_for_Nullable_CollectionExtensionMsg_for_RoyaltyInfoResponse": { - "type": "object", - "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionExtensionMsg_for_RoyaltyInfoResponse" - }, - { - "type": "null" - } - ] - }, - "name": { - "type": [ - "string", - "null" - ] - }, - "symbol": { - "type": [ - "string", - "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" - }, - "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 - } - ] - }, - "RoyaltyInfoResponse": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "type": "string" - }, - "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" - } - } -} diff --git a/contracts/cw721-base/schema/instantiate_msg.json b/contracts/cw721-base/schema/instantiate_msg.json deleted file mode 100644 index e4852cd29..000000000 --- a/contracts/cw721-base/schema/instantiate_msg.json +++ /dev/null @@ -1,136 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "InstantiateMsg", - "type": "object", - "required": [ - "name", - "symbol" - ], - "properties": { - "collection_info_extension": { - "description": "Optional extension of the collection metadata", - "anyOf": [ - { - "$ref": "#/definitions/CollectionExtensionMsg_for_RoyaltyInfoResponse" - }, - { - "type": "null" - } - ] - }, - "creator": { - "description": "Sets the creator of collection. The creator is the only one eligible to update `CollectionInfo`.", - "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": { - "CollectionExtensionMsg_for_RoyaltyInfoResponse": { - "description": "NOTE: In case `info` is not provided in `create()` or `validate()` (like for migration), creator/minter assertion is skipped.", - "type": "object", - "properties": { - "description": { - "type": [ - "string", - "null" - ] - }, - "explicit_content": { - "type": [ - "boolean", - "null" - ] - }, - "external_link": { - "type": [ - "string", - "null" - ] - }, - "image": { - "type": [ - "string", - "null" - ] - }, - "royalty_info": { - "anyOf": [ - { - "$ref": "#/definitions/RoyaltyInfoResponse" - }, - { - "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" - }, - "RoyaltyInfoResponse": { - "type": "object", - "required": [ - "payment_address", - "share" - ], - "properties": { - "payment_address": { - "type": "string" - }, - "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-base/schema/migrate_msg.json b/contracts/cw721-base/schema/migrate_msg.json deleted file mode 100644 index f83fa9508..000000000 --- a/contracts/cw721-base/schema/migrate_msg.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "$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-base/schema/query_msg.json b/contracts/cw721-base/schema/query_msg.json deleted file mode 100644 index 6a76af6bb..000000000 --- a/contracts/cw721-base/schema/query_msg.json +++ /dev/null @@ -1,603 +0,0 @@ -{ - "$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 - }, - { - "description": "Deprecated: use GetCollectionInfoAndExtension instead! Will be removed in next release!", - "deprecated": true, - "type": "object", - "required": [ - "contract_info" - ], - "properties": { - "contract_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Returns `CollectionInfoAndExtensionResponse`", - "type": "object", - "required": [ - "get_collection_info_and_extension" - ], - "properties": { - "get_collection_info_and_extension": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "returns `AllInfoResponse` which contains contract, collection and nft details", - "type": "object", - "required": [ - "get_all_info" - ], - "properties": { - "get_all_info": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Returns `CollectionExtensionAttributes`", - "type": "object", - "required": [ - "get_collection_extension_attributes" - ], - "properties": { - "get_collection_extension_attributes": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Deprecated: use GetMinterOwnership instead! Will be removed in next release!", - "deprecated": true, - "type": "object", - "required": [ - "ownership" - ], - "properties": { - "ownership": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Return the minter Deprecated: use GetMinterOwnership instead! Will be removed in next release!", - "deprecated": true, - "type": "object", - "required": [ - "minter" - ], - "properties": { - "minter": { - "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 - }, - { - "type": "object", - "required": [ - "get_nft_by_extension" - ], - "properties": { - "get_nft_by_extension": { - "type": "object", - "properties": { - "extension": { - "anyOf": [ - { - "$ref": "#/definitions/Empty" - }, - { - "type": "null" - } - ] - }, - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "start_after": { - "type": [ - "string", - "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" - ] - }, - "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": "Custom msg query. Default implementation returns an empty binary.", - "type": "object", - "required": [ - "extension" - ], - "properties": { - "extension": { - "type": "object", - "required": [ - "msg" - ], - "properties": { - "msg": { - "$ref": "#/definitions/Empty" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "get_collection_extension" - ], - "properties": { - "get_collection_extension": { - "type": "object", - "properties": { - "msg": { - "anyOf": [ - { - "$ref": "#/definitions/CollectionExtension_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" - }, - "CollectionExtension_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/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index 23c1ecd2c..000faedea 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -8,10 +8,11 @@ pub use cw721::*; // These types are re-exported so that contracts interacting with this // one don't need a direct dependency on cw_ownable to use the API. // -// `Action` is used in `Cw721ExecuteMsg::UpdateMinterOwnership` and `Cw721ExecuteMsg::UpdateCreatorOwnership`, `Ownership` is -// used in `Cw721QueryMsg::GetMinterOwnership`, `Cw721QueryMsg::GetCreatorOwnership`, and `OwnershipError` is used in -// `Cw721ContractError::Ownership`. +// `Action` is used in `ExecuteMsg::UpdateMinterOwnership` and `ExecuteMsg::UpdateCreatorOwnership`, `Ownership` is +// used in `QueryMsg::GetMinterOwnership`, `QueryMsg::GetCreatorOwnership`, and `OwnershipError` is used in +// `ContractError::Ownership`. pub use cw_ownable::{Action, Ownership, OwnershipError}; +use extension::Cw721BaseExtensions; // Version info for migration pub const CONTRACT_NAME: &str = "crates.io:cw721-base"; @@ -23,28 +24,26 @@ pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); )] pub type Extension = EmptyOptionalNftExtension; +pub type Cw721BaseContract<'a> = Cw721BaseExtensions<'a>; + pub mod entry { use super::*; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response}; - use cw721::{ - error::Cw721ContractError, - msg::Cw721InstantiateMsg, - traits::{Cw721Execute, Cw721Query}, - }; - use extension::Cw721BaseExtensions; - use msg::{ExecuteMsg, MigrateMsg, QueryMsg}; + use cw721::traits::{Cw721Execute, Cw721Query}; + use error::ContractError; + use msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721InstantiateMsg, - ) -> Result { - let contract = Cw721BaseExtensions::default(); + msg: InstantiateMsg, + ) -> Result { + let contract = Cw721BaseContract::default(); contract.instantiate_with_version(deps, &env, &info, msg, CONTRACT_NAME, CONTRACT_VERSION) } @@ -54,24 +53,20 @@ pub mod entry { env: Env, info: MessageInfo, msg: ExecuteMsg, - ) -> Result { - let contract = Cw721BaseExtensions::default(); + ) -> Result { + let contract = Cw721BaseContract::default(); contract.execute(deps, &env, &info, msg) } #[cfg_attr(not(feature = "library"), entry_point)] - pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { - let contract = Cw721BaseExtensions::default(); + pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { + let contract = Cw721BaseContract::default(); contract.query(deps, &env, msg) } #[cfg_attr(not(feature = "library"), entry_point)] - pub fn migrate( - deps: DepsMut, - env: Env, - msg: MigrateMsg, - ) -> Result { - let contract = Cw721BaseExtensions::default(); + pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> Result { + let contract = Cw721BaseContract::default(); contract.migrate(deps, env, msg, CONTRACT_NAME, CONTRACT_VERSION) } } @@ -85,7 +80,6 @@ mod tests { Empty, }; use cw721::traits::{Cw721Execute, Cw721Query}; - use extension::Cw721BaseExtensions; use msg::{ExecuteMsg, InstantiateMsg}; const CREATOR: &str = "creator"; @@ -94,7 +88,7 @@ mod tests { #[test] fn use_empty_metadata_extension() { let mut deps = mock_dependencies(); - let contract = Cw721BaseExtensions::default(); + let contract = Cw721BaseContract::default(); let info = mock_info(CREATOR, &[]); let init_msg = InstantiateMsg { name: "SpaceShips".to_string(), diff --git a/contracts/cw721-metadata-onchain/examples/schema.rs b/contracts/cw721-metadata-onchain/examples/schema.rs index 51eaeb1aa..73e575eba 100644 --- a/contracts/cw721-metadata-onchain/examples/schema.rs +++ b/contracts/cw721-metadata-onchain/examples/schema.rs @@ -1,12 +1,18 @@ -use cosmwasm_schema::write_api; - -use cw721_metadata_onchain::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cosmwasm_schema::{remove_schemas, write_api}; +use cw721_metadata_onchain::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(); + write_api! { instantiate: InstantiateMsg, execute: ExecuteMsg, query: QueryMsg, - migrate: cw721::msg::Cw721MigrateMsg, + migrate: MigrateMsg, } } diff --git a/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json b/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json index 3fb971a1b..e4071be51 100644 --- a/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json +++ b/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json @@ -1087,6 +1087,20 @@ }, "additionalProperties": false }, + { + "description": "Returns `AllCollectionInfoResponse`", + "type": "object", + "required": [ + "get_config" + ], + "properties": { + "get_config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "Returns `CollectionInfoAndExtensionResponse`", "type": "object", @@ -2602,6 +2616,285 @@ } } }, + "get_config": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse_for_Nullable_CollectionExtension_for_RoyaltyInfo", + "description": "This is a wrapper around CollectionInfo that includes the extension.", + "type": "object", + "required": [ + "collection_info", + "contract_info", + "creator_ownership", + "minter_ownership", + "num_tokens" + ], + "properties": { + "collection_extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "collection_info": { + "$ref": "#/definitions/CollectionInfo" + }, + "contract_info": { + "$ref": "#/definitions/ContractInfoResponse" + }, + "creator_ownership": { + "$ref": "#/definitions/Ownership_for_Addr" + }, + "minter_ownership": { + "$ref": "#/definitions/Ownership_for_Addr" + }, + "num_tokens": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "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" + }, + "CollectionExtension_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 + }, + "CollectionInfo": { + "type": "object", + "required": [ + "name", + "symbol", + "updated_at" + ], + "properties": { + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + "ContractInfoResponse": { + "type": "object", + "required": [ + "code_id", + "creator", + "pinned" + ], + "properties": { + "admin": { + "description": "admin who can run migrations (if any)", + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "creator": { + "description": "address that instantiated this contract", + "type": "string" + }, + "ibc_port": { + "description": "set if this contract has bound an IBC port", + "type": [ + "string", + "null" + ] + }, + "pinned": { + "description": "if set, the contract is pinned to the cache, and thus uses less gas when called", + "type": "boolean" + } + } + }, + "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" + }, + "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 + } + ] + }, + "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 + }, + "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_creator_ownership": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Ownership_for_Addr", diff --git a/contracts/cw721-metadata-onchain/src/error.rs b/contracts/cw721-metadata-onchain/src/error.rs new file mode 100644 index 000000000..3b882f5fa --- /dev/null +++ b/contracts/cw721-metadata-onchain/src/error.rs @@ -0,0 +1,2 @@ +// expose so other libs dont need to import cw721 +pub use cw721::error::Cw721ContractError as ContractError; diff --git a/contracts/cw721-metadata-onchain/src/lib.rs b/contracts/cw721-metadata-onchain/src/lib.rs index 5efab6434..f869a800f 100644 --- a/contracts/cw721-metadata-onchain/src/lib.rs +++ b/contracts/cw721-metadata-onchain/src/lib.rs @@ -1,15 +1,14 @@ -use cosmwasm_std::Empty; +pub mod error; +pub mod msg; +pub mod state; + use cw721::traits::{Cw721Execute, Cw721Query}; -use cw721::{ - DefaultOptionalCollectionExtension, DefaultOptionalCollectionExtensionMsg, - DefaultOptionalNftExtension, DefaultOptionalNftExtensionMsg, -}; // Version info for migration const CONTRACT_NAME: &str = "crates.io:cw721-metadata-onchain"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -/// This is an opionated `cw721-base` explicitly defining `NftExtension` for metadata on chain for ease of use. +/// This is an opionated `cw721` explicitly defining `NftExtension` for metadata on chain for ease of use. /// There are 2 possibiities for using metadata on chain: /// /// cw721-metadata-onchain: @@ -18,7 +17,7 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); /// // instantiate: /// let contract = Cw721MetadataContract::default(); /// let info = mock_info(CREATOR, &[]); -/// let init_msg = Cw721InstantiateMsg { +/// let init_msg = InstantiateMsg { /// name: "SpaceShips".to_string(), /// symbol: "SPACE".to_string(), /// collection_info_extension: None, @@ -48,66 +47,7 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); /// }; /// // ... /// ``` -/// -/// cw721-base with metadata onchain: -/// ```rust -/// // instantiate: -/// let contract = Cw721Contract::< -/// DefaultOptionalNftExtension, // use `Option` for no nft metadata -/// DefaultOptionalNftExtensionMsg, // use `Option` for no nft metadata -/// DefaultOptionalCollectionExtension, // use `Option` for no collection metadata -/// DefaultOptionalCollectionExtensionMsg, // use `Option` for no collection metadata -/// Empty, -/// Empty, -/// Empty, -/// >::default(); -/// let info = mock_info(CREATOR, &[]); -/// let init_msg = Cw721InstantiateMsg { -/// name: "SpaceShips".to_string(), -/// symbol: "SPACE".to_string(), -/// collection_info_extension: None, -/// minter: None, -/// creator: None, -/// withdraw_address: None, -/// }; -/// //... -/// // mint: -/// let token_id = "Enterprise"; -/// let token_uri = Some("https://starships.example.com/Starship/Enterprise.json".into()); -/// let extension = Some(NftExtensionMsg { -/// description: Some("description1".into()), -/// name: Some("name1".to_string()), -/// attributes: Some(vec![Trait { -/// display_type: None, -/// trait_type: "type1".to_string(), -/// value: "value1".to_string(), -/// }]), -/// ..NftExtensionMsg::default() -/// }); -/// let exec_msg = Cw721ExecuteMsg::< -/// DefaultOptionalNftExtensionMsg, -/// DefaultOptionalCollectionExtensionMsg, -/// Empty, -/// >::Mint { -/// token_id: token_id.to_string(), -/// owner: "john".to_string(), -/// token_uri: token_uri.clone(), -/// extension: extension.clone(), // use `extension: None` for no metadata -/// }; -/// //... -/// ``` pub type Cw721MetadataContract<'a> = cw721::extension::Cw721OnchainExtensions<'a>; -pub type InstantiateMsg = cw721::msg::Cw721InstantiateMsg; -pub type ExecuteMsg = cw721::msg::Cw721ExecuteMsg< - DefaultOptionalNftExtensionMsg, - DefaultOptionalCollectionExtensionMsg, - Empty, ->; -pub type QueryMsg = cw721::msg::Cw721QueryMsg< - DefaultOptionalNftExtension, - DefaultOptionalCollectionExtension, - Empty, ->; pub mod entry { use super::*; @@ -115,16 +55,17 @@ pub mod entry { #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response}; - use cw721::error::Cw721ContractError; - use cw721::msg::{Cw721InstantiateMsg, Cw721MigrateMsg}; + use cw721::msg::Cw721MigrateMsg; + use error::ContractError; + use msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( mut deps: DepsMut, env: Env, info: MessageInfo, - msg: Cw721InstantiateMsg, - ) -> Result { + msg: InstantiateMsg, + ) -> Result { Cw721MetadataContract::default().instantiate_with_version( deps.branch(), &env, @@ -141,12 +82,12 @@ pub mod entry { env: Env, info: MessageInfo, msg: ExecuteMsg, - ) -> Result { + ) -> Result { Cw721MetadataContract::default().execute(deps, &env, &info, msg) } #[cfg_attr(not(feature = "library"), entry_point)] - pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { + pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { Cw721MetadataContract::default().query(deps, &env, msg) } @@ -155,7 +96,7 @@ pub mod entry { deps: DepsMut, env: Env, msg: Cw721MigrateMsg, - ) -> Result { + ) -> Result { let contract = Cw721MetadataContract::default(); contract.migrate(deps, env, msg, CONTRACT_NAME, CONTRACT_VERSION) } @@ -166,11 +107,8 @@ mod tests { use super::*; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cw721::{ - msg::{Cw721InstantiateMsg, NftExtensionMsg}, - state::Trait, - NftExtension, - }; + use cw721::{msg::NftExtensionMsg, state::Trait, NftExtension}; + use msg::{ExecuteMsg, InstantiateMsg}; const CREATOR: &str = "creator"; @@ -184,7 +122,7 @@ mod tests { deps.as_mut(), mock_env(), mock_info("sender", &[]), - Cw721InstantiateMsg { + InstantiateMsg { name: "collection_name".into(), symbol: "collection_symbol".into(), collection_info_extension: None, @@ -205,7 +143,7 @@ mod tests { let contract = Cw721MetadataContract::default(); let info = mock_info(CREATOR, &[]); - let init_msg = Cw721InstantiateMsg { + let init_msg = InstantiateMsg { name: "SpaceShips".to_string(), symbol: "SPACE".to_string(), collection_info_extension: None, diff --git a/contracts/cw721-metadata-onchain/src/msg.rs b/contracts/cw721-metadata-onchain/src/msg.rs new file mode 100644 index 000000000..9bfae4684 --- /dev/null +++ b/contracts/cw721-metadata-onchain/src/msg.rs @@ -0,0 +1,20 @@ +use cosmwasm_std::Empty; + +use cw721::{ + msg::Cw721MigrateMsg, DefaultOptionalCollectionExtension, + DefaultOptionalCollectionExtensionMsg, DefaultOptionalNftExtension, + DefaultOptionalNftExtensionMsg, +}; + +pub type InstantiateMsg = cw721::msg::Cw721InstantiateMsg; +pub type ExecuteMsg = cw721::msg::Cw721ExecuteMsg< + DefaultOptionalNftExtensionMsg, + DefaultOptionalCollectionExtensionMsg, + Empty, +>; +pub type QueryMsg = cw721::msg::Cw721QueryMsg< + DefaultOptionalNftExtension, + DefaultOptionalCollectionExtension, + Empty, +>; +pub type MigrateMsg = Cw721MigrateMsg; diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 32f94639a..96fc62f0c 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -6,7 +6,7 @@ use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, s use cosmwasm_std::Empty; use cw721::{ msg::{ - AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, + ConfigResponse, AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, CollectionInfoAndExtensionResponse, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, Cw721QueryMsg, MinterResponse, NftInfoResponse, NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, @@ -68,6 +68,11 @@ fn main() { &out_dir, "CollectionInfo", ); + export_schema_with_title( + &schema_for!(ConfigResponse), + &out_dir, + "AllCollectionInfo", + ); export_schema(&schema_for!(OwnerOfResponse), &out_dir); export_schema(&schema_for!(MinterResponse), &out_dir); export_schema(&schema_for!(NumTokensResponse), &out_dir); diff --git a/packages/cw721/schema/all_collection_info.json b/packages/cw721/schema/all_collection_info.json new file mode 100644 index 000000000..20d82c17c --- /dev/null +++ b/packages/cw721/schema/all_collection_info.json @@ -0,0 +1,279 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllCollectionInfo", + "description": "This is a wrapper around CollectionInfo that includes the extension.", + "type": "object", + "required": [ + "collection_info", + "contract_info", + "creator_ownership", + "minter_ownership", + "num_tokens" + ], + "properties": { + "collection_extension": { + "anyOf": [ + { + "$ref": "#/definitions/CollectionExtension_for_RoyaltyInfo" + }, + { + "type": "null" + } + ] + }, + "collection_info": { + "$ref": "#/definitions/CollectionInfo" + }, + "contract_info": { + "$ref": "#/definitions/ContractInfoResponse" + }, + "creator_ownership": { + "$ref": "#/definitions/Ownership_for_Addr" + }, + "minter_ownership": { + "$ref": "#/definitions/Ownership_for_Addr" + }, + "num_tokens": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "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" + }, + "CollectionExtension_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 + }, + "CollectionInfo": { + "type": "object", + "required": [ + "name", + "symbol", + "updated_at" + ], + "properties": { + "name": { + "type": "string" + }, + "symbol": { + "type": "string" + }, + "updated_at": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + "ContractInfoResponse": { + "type": "object", + "required": [ + "code_id", + "creator", + "pinned" + ], + "properties": { + "admin": { + "description": "admin who can run migrations (if any)", + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "creator": { + "description": "address that instantiated this contract", + "type": "string" + }, + "ibc_port": { + "description": "set if this contract has bound an IBC port", + "type": [ + "string", + "null" + ] + }, + "pinned": { + "description": "if set, the contract is pinned to the cache, and thus uses less gas when called", + "type": "boolean" + } + } + }, + "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" + }, + "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 + } + ] + }, + "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 + }, + "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/cw721_query_msg.json b/packages/cw721/schema/cw721_query_msg.json index 210684278..b862c7ab2 100644 --- a/packages/cw721/schema/cw721_query_msg.json +++ b/packages/cw721/schema/cw721_query_msg.json @@ -195,6 +195,20 @@ }, "additionalProperties": false }, + { + "description": "Returns `AllCollectionInfoResponse`", + "type": "object", + "required": [ + "get_config" + ], + "properties": { + "get_config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "Returns `CollectionInfoAndExtensionResponse`", "type": "object", diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index 02a3cc5d6..ba3f97db0 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -200,6 +200,10 @@ pub enum Cw721QueryMsg< /// Deprecated: use GetCollectionInfoAndExtension instead! Will be removed in next release! ContractInfo {}, + /// Returns `AllCollectionInfoResponse` + #[returns(ConfigResponse)] + GetConfig {}, + /// Returns `CollectionInfoAndExtensionResponse` #[returns(CollectionInfoAndExtensionResponse)] GetCollectionInfoAndExtension {}, @@ -587,6 +591,18 @@ impl From for RoyaltyInfoResponse { } } +/// This is a wrapper around CollectionInfo that includes the extension. +#[cw_serde] +pub struct ConfigResponse { + pub num_tokens: u64, + pub minter_ownership: Ownership, + pub creator_ownership: Ownership, + pub withdraw_address: Option, + pub collection_info: CollectionInfo, + pub collection_extension: TCollectionExtension, + pub contract_info: ContractInfoResponse, +} + /// This is a wrapper around CollectionInfo that includes the extension. #[cw_serde] pub struct CollectionInfoAndExtensionResponse { diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index 987d5326b..2273d9d78 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -12,8 +12,8 @@ use crate::{ }, msg::{ AllInfoResponse, AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, - CollectionInfoAndExtensionResponse, MinterResponse, NftInfoResponse, NumTokensResponse, - OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, + CollectionInfoAndExtensionResponse, ConfigResponse, MinterResponse, NftInfoResponse, + NumTokensResponse, OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }, state::{ Approval, CollectionExtensionAttributes, CollectionInfo, Cw721Config, NftInfo, CREATOR, @@ -92,6 +92,31 @@ pub fn query_collection_extension_attributes( ) } +pub fn query_config( + deps: Deps, + contract_addr: impl Into, +) -> Result, Cw721ContractError> +where + TCollectionExtension: Cw721State + FromAttributesState, +{ + let collection_info = query_collection_info(deps.storage)?; + let attributes = query_collection_extension_attributes(deps)?; + let collection_extension = FromAttributesState::from_attributes_state(&attributes)?; + let num_tokens = query_num_tokens(deps.storage)?.count; + let minter_ownership = query_minter_ownership(deps.storage)?; + let creator_ownership = query_creator_ownership(deps.storage)?; + let withdraw_address = query_withdraw_address(deps)?; + let contract_info = deps.querier.query_wasm_contract_info(contract_addr)?; + Ok(ConfigResponse { + num_tokens, + minter_ownership, + creator_ownership, + collection_info, + collection_extension, + withdraw_address, + contract_info, + }) +} pub fn query_collection_info_and_extension( deps: Deps, ) -> Result, Cw721ContractError> diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index ef8ad50d1..cb1b95656 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -51,7 +51,7 @@ pub struct Cw721Config< /// Note: replaces deprecated/legacy key "nft_info"! pub collection_info: Item<'a, CollectionInfo>, pub collection_extension: Map<'a, String, Attribute>, - pub token_count: Item<'a, u64>, + pub num_tokens: 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>, @@ -84,7 +84,7 @@ where fn new( collection_info_key: &'a str, collection_info_extension_key: &'a str, - token_count_key: &'a str, + num_tokens_key: &'a str, operator_key: &'a str, nft_info_key: &'a str, nft_info_owner_key: &'a str, @@ -95,7 +95,7 @@ where }; Self { collection_info: Item::new(collection_info_key), - token_count: Item::new(token_count_key), + num_tokens: Item::new(num_tokens_key), operators: Map::new(operator_key), nft_info: IndexedMap::new(nft_info_key, indexes), withdraw_address: Item::new(withdraw_address_key), @@ -104,18 +104,18 @@ where } pub fn token_count(&self, storage: &dyn Storage) -> StdResult { - Ok(self.token_count.may_load(storage)?.unwrap_or_default()) + Ok(self.num_tokens.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)?; + self.num_tokens.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)?; + self.num_tokens.save(storage, &val)?; Ok(val) } } diff --git a/packages/cw721/src/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index 2463e7679..1f43ccafe 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -2,10 +2,11 @@ use crate::{ error::Cw721ContractError, extension::Cw721OnchainExtensions, msg::{ - CollectionExtensionMsg, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, - Cw721QueryMsg, MinterResponse, NumTokensResponse, OwnerOfResponse, RoyaltyInfoResponse, + CollectionExtensionMsg, ConfigResponse, Cw721ExecuteMsg, Cw721InstantiateMsg, + Cw721MigrateMsg, Cw721QueryMsg, MinterResponse, NumTokensResponse, OwnerOfResponse, + RoyaltyInfoResponse, }, - state::{NftExtension, Trait}, + state::{CollectionInfo, NftExtension, Trait}, traits::{Cw721Execute, Cw721Query}, DefaultOptionalCollectionExtension, DefaultOptionalCollectionExtensionMsg, DefaultOptionalNftExtension, DefaultOptionalNftExtensionMsg, NftExtensionMsg, @@ -334,6 +335,22 @@ fn query_nft_info( .unwrap() } +fn query_all_collection_info( + querier: QuerierWrapper, + cw721: &Addr, +) -> ConfigResponse { + querier + .query_wasm_smart( + cw721, + &Cw721QueryMsg::< + DefaultOptionalNftExtension, + DefaultOptionalCollectionExtension, + Empty, + >::GetConfig {}, + ) + .unwrap() +} + fn mint_transfer_and_burn(app: &mut MockApp, cw721: Addr, sender: Addr, token_id: String) { app.execute_contract( sender.clone(), @@ -2499,3 +2516,142 @@ fn test_update_nft_metadata() { .unwrap(); assert_eq!(num_tokens.count, 1); } + +#[test] +fn test_queries() { + // --- setup --- + let mut app = new(); + let admin = app.api().addr_make(ADMIN_ADDR); + let code_id = app.store_code(cw721_base_latest_contract()); + let creator = app.api().addr_make(CREATOR_ADDR); + let minter_addr = app.api().addr_make(MINTER_ADDR); + let withdraw_addr = app.api().addr_make(OTHER2_ADDR); + let cw721 = app + .instantiate_contract( + code_id, + creator.clone(), + &Cw721InstantiateMsg:: { + name: "collection".to_string(), + symbol: "symbol".to_string(), + minter: Some(minter_addr.to_string()), + creator: None, // in case of none, sender is creator + collection_info_extension: None, + withdraw_address: Some(withdraw_addr.to_string()), + }, + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); + // mint + let nft_owner = app.api().addr_make(NFT_OWNER_ADDR); + let nft_metadata_msg = NftExtensionMsg { + image: Some("ipfs://foo.bar/image.png".to_string()), + image_data: Some("image data".to_string()), + external_url: Some("https://github.com".to_string()), + description: Some("description".to_string()), + name: Some("name".to_string()), + attributes: Some(vec![Trait { + trait_type: "trait_type".to_string(), + value: "value".to_string(), + display_type: Some("display_type".to_string()), + }]), + background_color: Some("background_color".to_string()), + animation_url: Some("ssl://animation_url".to_string()), + youtube_url: Some("file://youtube_url".to_string()), + }; + app.execute_contract( + minter_addr.clone(), + cw721.clone(), + &Cw721ExecuteMsg::< + DefaultOptionalNftExtensionMsg, + DefaultOptionalCollectionExtensionMsg, + Empty, + >::Mint { + token_id: "1".to_string(), + owner: nft_owner.to_string(), + token_uri: Some("ipfs://foo.bar/metadata.json".to_string()), + extension: Some(nft_metadata_msg.clone()), + }, + &[], + ) + .unwrap(); + + // check nft info + let nft_info = query_nft_info(app.wrap(), &cw721, "1".to_string()); + assert_eq!( + nft_info.token_uri, + Some("ipfs://foo.bar/metadata.json".to_string()) + ); + assert_eq!( + nft_info.extension, + Some(NftExtension { + image: Some("ipfs://foo.bar/image.png".to_string()), + image_data: Some("image data".to_string()), + external_url: Some("https://github.com".to_string()), + description: Some("description".to_string()), + name: Some("name".to_string()), + attributes: Some(vec![Trait { + trait_type: "trait_type".to_string(), + value: "value".to_string(), + display_type: Some("display_type".to_string()), + }]), + background_color: Some("background_color".to_string()), + animation_url: Some("ssl://animation_url".to_string()), + youtube_url: Some("file://youtube_url".to_string()), + }) + ); + // check num tokens + let num_tokens: NumTokensResponse = app + .wrap() + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::< + DefaultOptionalNftExtension, + DefaultOptionalCollectionExtension, + Empty, + >::NumTokens {}, + ) + .unwrap(); + assert_eq!(num_tokens.count, 1); + // check withdraw address + let withdraw_addr_result: Option = app + .wrap() + .query_wasm_smart( + &cw721, + &Cw721QueryMsg::< + DefaultOptionalNftExtension, + DefaultOptionalCollectionExtension, + Empty, + >::GetWithdrawAddress {}, + ) + .unwrap(); + assert_eq!(withdraw_addr_result, Some(withdraw_addr.to_string())); + // check all collection info + let all_collection_info = query_all_collection_info(app.wrap(), &cw721); + let contract_info = app.wrap().query_wasm_contract_info(&cw721).unwrap(); + assert_eq!( + all_collection_info, + ConfigResponse { + minter_ownership: Ownership { + owner: Some(minter_addr), + pending_expiry: None, + pending_owner: None, + }, + creator_ownership: Ownership { + owner: Some(creator), + pending_expiry: None, + pending_owner: None, + }, + collection_info: CollectionInfo { + name: "collection".to_string(), + symbol: "symbol".to_string(), + updated_at: all_collection_info.collection_info.updated_at, + }, + collection_extension: None, + num_tokens: 1, + withdraw_address: Some(withdraw_addr.into_string()), + contract_info + } + ); +} diff --git a/packages/cw721/src/traits.rs b/packages/cw721/src/traits.rs index 0ff3d2ef1..f8007cd75 100644 --- a/packages/cw721/src/traits.rs +++ b/packages/cw721/src/traits.rs @@ -35,8 +35,8 @@ use crate::{ Attribute, }; use crate::{ - msg::AllInfoResponse, - query::{query_all_info, query_nft_by_extension}, + msg::{AllInfoResponse, ConfigResponse}, + query::{query_all_info, query_config, query_nft_by_extension}, Approval, }; @@ -495,6 +495,9 @@ pub trait Cw721Query< Cw721QueryMsg::ContractInfo {} => Ok(to_json_binary( &self.query_collection_info_and_extension(deps)?, )?), + Cw721QueryMsg::GetConfig {} => Ok(to_json_binary( + &self.query_all_collection_info(deps, env.contract.address.to_string())?, + )?), Cw721QueryMsg::GetCollectionInfoAndExtension {} => Ok(to_json_binary( &self.query_collection_info_and_extension(deps)?, )?), @@ -637,6 +640,17 @@ pub trait Cw721Query< query_collection_extension_attributes(deps) } + fn query_all_collection_info( + &self, + deps: Deps, + contract_addr: impl Into, + ) -> Result, Cw721ContractError> + where + TCollectionExtension: FromAttributesState, + { + query_config(deps, contract_addr) + } + fn query_collection_info_and_extension( &self, deps: Deps, From 724e379325ec153e5677269e5782e740c655ebea Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 20:59:46 +0200 Subject: [PATCH 08/13] rename --- .cargo/{config => config.toml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .cargo/{config => config.toml} (100%) diff --git a/.cargo/config b/.cargo/config.toml similarity index 100% rename from .cargo/config rename to .cargo/config.toml From 6b5f3c009435fc8401533fd62ffd894e46dd2a67 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 21:15:40 +0200 Subject: [PATCH 09/13] cleanup --- packages/cw721/src/execute.rs | 22 ++++++++++++---------- packages/cw721/src/traits.rs | 9 +++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/cw721/src/execute.rs b/packages/cw721/src/execute.rs index a1ee90ab3..1bba2ae4b 100644 --- a/packages/cw721/src/execute.rs +++ b/packages/cw721/src/execute.rs @@ -127,7 +127,7 @@ where 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)?; + check_can_send(deps.as_ref(), env, info.sender.as_str(), &token)?; // set owner and remove existing approvals token.owner = deps.api.addr_validate(recipient)?; token.approvals = vec![]; @@ -203,7 +203,7 @@ where 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)?; + check_can_approve(deps.as_ref(), env, info.sender.as_str(), &token)?; // update the approval list (remove any for the same spender before adding) let spender_addr = deps.api.addr_validate(spender)?; @@ -301,7 +301,7 @@ pub fn burn_nft( ) -> Result, Cw721ContractError> { let config = Cw721Config::>::default(); let token = config.nft_info.load(deps.storage, &token_id)?; - check_can_send(deps.as_ref(), env, info, &token)?; + check_can_send(deps.as_ref(), env, info.sender.as_str(), &token)?; config.nft_info.remove(deps.storage, &token_id)?; config.decrement_tokens(deps.storage)?; @@ -503,21 +503,22 @@ pub fn withdraw_funds( pub fn check_can_approve( deps: Deps, env: &Env, - info: &MessageInfo, + sender: &str, token: &NftInfo, ) -> Result<(), Cw721ContractError> where TNftExtension: Cw721State, { + let sender = deps.api.addr_validate(sender)?; // owner can approve - if token.owner == info.sender { + if token.owner == sender { return Ok(()); } // operator can approve let config = Cw721Config::::default(); let op = config .operators - .may_load(deps.storage, (&token.owner, &info.sender))?; + .may_load(deps.storage, (&token.owner, &sender))?; match op { Some(ex) => { if ex.is_expired(&env.block) { @@ -534,11 +535,12 @@ where pub fn check_can_send( deps: Deps, env: &Env, - info: &MessageInfo, + sender: &str, token: &NftInfo, ) -> Result<(), Cw721ContractError> { + let sender = deps.api.addr_validate(sender)?; // owner can send - if token.owner == info.sender { + if token.owner == sender { return Ok(()); } @@ -546,7 +548,7 @@ pub fn check_can_send( if token .approvals .iter() - .any(|apr| apr.spender == info.sender && !apr.is_expired(&env.block)) + .any(|apr| apr.spender == sender && !apr.is_expired(&env.block)) { return Ok(()); } @@ -556,7 +558,7 @@ pub fn check_can_send( 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))?; + .may_load(deps.storage, (&token.owner, &sender))?; match op { Some(ex) => { diff --git a/packages/cw721/src/traits.rs b/packages/cw721/src/traits.rs index f8007cd75..dc1b35325 100644 --- a/packages/cw721/src/traits.rs +++ b/packages/cw721/src/traits.rs @@ -925,6 +925,15 @@ pub trait Cw721Calls< Ok(res.count) } + /// This is a helper to get the metadata and extension data in one call + fn config( + &self, + querier: &QuerierWrapper, + ) -> StdResult> { + let req = Cw721QueryMsg::GetConfig {}; + self.query(querier, req) + } + /// This is a helper to get the metadata and extension data in one call fn collection_info( &self, From b5c27c5b786ac546a0c64905fa0d764d03449639 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 21:18:29 +0200 Subject: [PATCH 10/13] schema --- packages/cw721/examples/schema.rs | 10 +++++----- .../schema/{all_collection_info.json => config.json} | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) rename packages/cw721/schema/{all_collection_info.json => config.json} (99%) diff --git a/packages/cw721/examples/schema.rs b/packages/cw721/examples/schema.rs index 96fc62f0c..fe63d19fa 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -6,10 +6,10 @@ use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, s use cosmwasm_std::Empty; use cw721::{ msg::{ - ConfigResponse, AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, - CollectionInfoAndExtensionResponse, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, - Cw721QueryMsg, MinterResponse, NftInfoResponse, NumTokensResponse, OperatorResponse, - OperatorsResponse, OwnerOfResponse, TokensResponse, + AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, + CollectionInfoAndExtensionResponse, ConfigResponse, Cw721ExecuteMsg, Cw721InstantiateMsg, + Cw721MigrateMsg, Cw721QueryMsg, MinterResponse, NftInfoResponse, NumTokensResponse, + OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }, receiver::Cw721ReceiveMsg, DefaultOptionalCollectionExtension, DefaultOptionalNftExtension, @@ -71,7 +71,7 @@ fn main() { export_schema_with_title( &schema_for!(ConfigResponse), &out_dir, - "AllCollectionInfo", + "Config", ); export_schema(&schema_for!(OwnerOfResponse), &out_dir); export_schema(&schema_for!(MinterResponse), &out_dir); diff --git a/packages/cw721/schema/all_collection_info.json b/packages/cw721/schema/config.json similarity index 99% rename from packages/cw721/schema/all_collection_info.json rename to packages/cw721/schema/config.json index 20d82c17c..a430572bb 100644 --- a/packages/cw721/schema/all_collection_info.json +++ b/packages/cw721/schema/config.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "AllCollectionInfo", + "title": "Config", "description": "This is a wrapper around CollectionInfo that includes the extension.", "type": "object", "required": [ From d733ffba1dad50a2618f3eec923b854f98356b17 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 21:30:03 +0200 Subject: [PATCH 11/13] cleanup --- packages/cw721/src/msg.rs | 4 ++-- packages/cw721/src/query.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index ba3f97db0..bcb0592d4 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -256,8 +256,8 @@ pub enum Cw721QueryMsg< include_expired: Option, }, - /// With Enumerable extension. - /// Returns all tokens owned by the given address, [] if unset. + /// Returns all tokens owned by the given address. + /// Same as `AllTokens` but with owner filter. #[returns(TokensResponse)] Tokens { owner: String, diff --git a/packages/cw721/src/query.rs b/packages/cw721/src/query.rs index 2273d9d78..960915dfb 100644 --- a/packages/cw721/src/query.rs +++ b/packages/cw721/src/query.rs @@ -379,14 +379,14 @@ pub fn query_all_tokens( 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::>::default() + let tokens = Cw721Config::>::default() .nft_info .range(deps.storage, start, None, Order::Ascending) .take(limit) .map(|item| item.map(|(k, _)| k)) - .collect(); + .collect::>>()?; - Ok(TokensResponse { tokens: tokens? }) + Ok(TokensResponse { tokens }) } pub fn query_all_nft_info( From 83a3cca401bd2e4282c8dcce06cc7ab568556207 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 21:34:23 +0200 Subject: [PATCH 12/13] cleanup --- contracts/cw721-base/schema/cw721-base.json | 4 ++-- .../cw721-metadata-onchain/schema/cw721-metadata-onchain.json | 4 ++-- packages/cw721/schema/cw721_query_msg.json | 4 ++-- packages/cw721/src/msg.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/cw721-base/schema/cw721-base.json b/contracts/cw721-base/schema/cw721-base.json index f61e702db..1f0840daf 100644 --- a/contracts/cw721-base/schema/cw721-base.json +++ b/contracts/cw721-base/schema/cw721-base.json @@ -999,7 +999,7 @@ "additionalProperties": false }, { - "description": "Returns `AllCollectionInfoResponse`", + "description": "Returns `ConfigResponse`", "type": "object", "required": [ "get_config" @@ -1201,7 +1201,7 @@ "additionalProperties": false }, { - "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", + "description": "Returns all tokens owned by the given address. Same as `AllTokens` but with owner filter.", "type": "object", "required": [ "tokens" diff --git a/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json b/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json index e4071be51..423f04c87 100644 --- a/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json +++ b/contracts/cw721-metadata-onchain/schema/cw721-metadata-onchain.json @@ -1088,7 +1088,7 @@ "additionalProperties": false }, { - "description": "Returns `AllCollectionInfoResponse`", + "description": "Returns `ConfigResponse`", "type": "object", "required": [ "get_config" @@ -1290,7 +1290,7 @@ "additionalProperties": false }, { - "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", + "description": "Returns all tokens owned by the given address. Same as `AllTokens` but with owner filter.", "type": "object", "required": [ "tokens" diff --git a/packages/cw721/schema/cw721_query_msg.json b/packages/cw721/schema/cw721_query_msg.json index b862c7ab2..7eec6ca2e 100644 --- a/packages/cw721/schema/cw721_query_msg.json +++ b/packages/cw721/schema/cw721_query_msg.json @@ -196,7 +196,7 @@ "additionalProperties": false }, { - "description": "Returns `AllCollectionInfoResponse`", + "description": "Returns `ConfigResponse`", "type": "object", "required": [ "get_config" @@ -398,7 +398,7 @@ "additionalProperties": false }, { - "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset.", + "description": "Returns all tokens owned by the given address. Same as `AllTokens` but with owner filter.", "type": "object", "required": [ "tokens" diff --git a/packages/cw721/src/msg.rs b/packages/cw721/src/msg.rs index bcb0592d4..7831762f2 100644 --- a/packages/cw721/src/msg.rs +++ b/packages/cw721/src/msg.rs @@ -200,7 +200,7 @@ pub enum Cw721QueryMsg< /// Deprecated: use GetCollectionInfoAndExtension instead! Will be removed in next release! ContractInfo {}, - /// Returns `AllCollectionInfoResponse` + /// Returns `ConfigResponse` #[returns(ConfigResponse)] GetConfig {}, From 40c9b9e8037d94a3042e6f6a89ef5b5aa23c7267 Mon Sep 17 00:00:00 2001 From: mr-t Date: Tue, 13 Aug 2024 21:59:47 +0200 Subject: [PATCH 13/13] cleanup --- packages/cw721/src/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index cb1b95656..c94e46d4a 100644 --- a/packages/cw721/src/state.rs +++ b/packages/cw721/src/state.rs @@ -3,7 +3,7 @@ use cosmwasm_std::{ from_json, to_json_binary, Addr, Binary, BlockInfo, Decimal, Deps, Empty, Env, MessageInfo, StdResult, Storage, Timestamp, }; -use cw_ownable::{OwnershipStore, OWNERSHIP_KEY}; +use cw_ownable::{OwnershipStore, OWNERSHIP}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; use cw_utils::Expiration; use serde::de::DeserializeOwned; @@ -16,7 +16,7 @@ use crate::{traits::StateFactory, NftExtensionMsg}; /// !!! 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); +pub const CREATOR: OwnershipStore = OWNERSHIP; /// - 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");