diff --git a/.cargo/config b/.cargo/config.toml similarity index 100% rename from .cargo/config rename to .cargo/config.toml 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/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 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/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..1f0840daf --- /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 `ConfigResponse`", + "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": "Returns all tokens owned by the given address. Same as `AllTokens` but with owner filter.", + "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..423f04c87 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 `ConfigResponse`", + "type": "object", + "required": [ + "get_config" + ], + "properties": { + "get_config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "Returns `CollectionInfoAndExtensionResponse`", "type": "object", @@ -1276,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" @@ -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/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/examples/schema.rs b/packages/cw721/examples/schema.rs index 32f94639a..fe63d19fa 100644 --- a/packages/cw721/examples/schema.rs +++ b/packages/cw721/examples/schema.rs @@ -7,9 +7,9 @@ use cosmwasm_std::Empty; use cw721::{ msg::{ AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, - CollectionInfoAndExtensionResponse, Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, - Cw721QueryMsg, MinterResponse, NftInfoResponse, NumTokensResponse, OperatorResponse, - OperatorsResponse, OwnerOfResponse, TokensResponse, + CollectionInfoAndExtensionResponse, ConfigResponse, Cw721ExecuteMsg, Cw721InstantiateMsg, + Cw721MigrateMsg, Cw721QueryMsg, MinterResponse, NftInfoResponse, NumTokensResponse, + OperatorResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }, receiver::Cw721ReceiveMsg, DefaultOptionalCollectionExtension, DefaultOptionalNftExtension, @@ -68,6 +68,11 @@ fn main() { &out_dir, "CollectionInfo", ); + export_schema_with_title( + &schema_for!(ConfigResponse), + &out_dir, + "Config", + ); 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/config.json b/packages/cw721/schema/config.json new file mode 100644 index 000000000..a430572bb --- /dev/null +++ b/packages/cw721/schema/config.json @@ -0,0 +1,279 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Config", + "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..7eec6ca2e 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 `ConfigResponse`", + "type": "object", + "required": [ + "get_config" + ], + "properties": { + "get_config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "Returns `CollectionInfoAndExtensionResponse`", "type": "object", @@ -384,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/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..1bba2ae4b 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 @@ -128,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![]; @@ -204,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)?; @@ -302,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)?; @@ -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") @@ -504,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) { @@ -535,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(()); } @@ -547,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(()); } @@ -557,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) => { @@ -649,7 +650,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/msg.rs b/packages/cw721/src/msg.rs index c7127b7c9..7831762f2 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 `ConfigResponse` + #[returns(ConfigResponse)] + GetConfig {}, + /// Returns `CollectionInfoAndExtensionResponse` #[returns(CollectionInfoAndExtensionResponse)] GetCollectionInfoAndExtension {}, @@ -252,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, @@ -386,8 +390,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 +459,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,25 +526,24 @@ 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) => { 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) @@ -551,8 +553,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> { @@ -574,6 +576,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(()) } } @@ -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 { @@ -627,8 +643,8 @@ where { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&CollectionInfoAndExtensionResponse>, ) -> Result, Cw721ContractError> { @@ -653,7 +669,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 +682,8 @@ where fn validate( &self, - deps: Option, - _env: Option<&Env>, + deps: Deps, + _env: &Env, info: Option<&MessageInfo>, _current: Option<&CollectionInfoAndExtensionResponse>, ) -> Result<(), Cw721ContractError> { @@ -679,7 +694,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 +890,8 @@ where { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, optional_current: Option<&NftInfo>, ) -> Result, Cw721ContractError> { @@ -898,10 +912,9 @@ 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! + owner: Addr::unchecked(&self.owner), // only for creation we use owner, but not for update! approvals: vec![], token_uri, extension, @@ -912,12 +925,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 @@ -931,6 +943,8 @@ where if let Some(token_uri) = token_uri { Url::parse(token_uri.as_str())?; } + // validate owner + deps.api.addr_validate(&self.owner)?; Ok(()) } } @@ -974,8 +988,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 +1046,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 +1055,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 +1063,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 +1100,8 @@ where { fn create( &self, - deps: Option, - env: Option<&Env>, + deps: Deps, + env: &Env, info: Option<&MessageInfo>, current: Option<&Option>, ) -> Result, Cw721ContractError> { @@ -1106,8 +1118,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/query.rs b/packages/cw721/src/query.rs index 987d5326b..960915dfb 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> @@ -354,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( diff --git a/packages/cw721/src/state.rs b/packages/cw721/src/state.rs index 8a081be4b..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"); @@ -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) } } @@ -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/testing/multi_tests.rs b/packages/cw721/src/testing/multi_tests.rs index 24bf333eb..1f43ccafe 100644 --- a/packages/cw721/src/testing/multi_tests.rs +++ b/packages/cw721/src/testing/multi_tests.rs @@ -2,29 +2,214 @@ use crate::{ error::Cw721ContractError, extension::Cw721OnchainExtensions, msg::{ - Cw721ExecuteMsg, Cw721InstantiateMsg, Cw721MigrateMsg, Cw721QueryMsg, MinterResponse, - NumTokensResponse, OwnerOfResponse, + 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, }; +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, 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::{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 OTHER1_ADDR: &str = "other"; +pub const OTHER2_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,23 @@ fn query_nft_info( .unwrap() } -fn mint_transfer_and_burn(app: &mut App, cw721: Addr, sender: Addr, token_id: String) { +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(), cw721.clone(), @@ -165,11 +368,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 +381,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 +395,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(OTHER1_ADDR); let cw721 = app .instantiate_contract( code_id, @@ -202,8 +408,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 +419,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 +593,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( @@ -435,7 +640,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(), @@ -526,13 +731,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,14 +762,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(), } @@ -591,7 +798,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 +846,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 +863,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( @@ -704,7 +910,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(), @@ -795,13 +1001,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 +1032,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 +1068,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 +1116,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 +1133,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( @@ -973,7 +1180,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(), @@ -1064,13 +1271,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 +1302,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 +1338,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 +1386,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 +1403,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( @@ -1242,7 +1450,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(), @@ -1333,13 +1541,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 +1572,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 +1608,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 +1656,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( @@ -1463,53 +1672,195 @@ 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 = App::default(); - let admin = || Addr::unchecked("admin"); +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 code_id_latest = app.store_code(cw721_base_latest_contract()); + let cw721 = app + .instantiate_contract( + code_id_latest, + admin.clone(), + &init_msg.clone(), + &[], + "cw721-base", + Some(admin.to_string()), + ) + .unwrap(); - let code_id_latest = app.store_code(cw721_base_latest_contract()); + // 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: 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 code_id_latest = app.store_code(cw721_base_latest_contract()); - let cw721 = app - .instantiate_contract( - code_id_latest, - admin(), - &v16::InstantiateMsg { - name: "collection".to_string(), - symbol: "symbol".to_string(), - minter: admin().into_string(), - }, - &[], - "cw721-base", - Some(admin().into_string()), - ) - .unwrap(); + 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()); + // 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] 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 +1868,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 +1879,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 +1896,7 @@ fn test_update_nft_metadata() { youtube_url: Some("file://youtube_url".to_string()), }; app.execute_contract( - minter, + minter_addr, cw721.clone(), &Cw721ExecuteMsg::< DefaultOptionalNftExtensionMsg, @@ -2166,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/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(), diff --git a/packages/cw721/src/traits.rs b/packages/cw721/src/traits.rs index 87f98e8af..dc1b35325 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, }; @@ -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, @@ -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, @@ -911,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,