diff --git a/contracts/cw2981-royalties/examples/schema.rs b/contracts/cw2981-royalties/examples/schema.rs index 38bc736b4..3535e1d59 100644 --- a/contracts/cw2981-royalties/examples/schema.rs +++ b/contracts/cw2981-royalties/examples/schema.rs @@ -2,6 +2,7 @@ use std::env::current_dir; use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; +use cosmwasm_std::Empty; use cw721::{ AllNftInfoResponse, ContractInfoResponse, NftInfoResponse, NumTokensResponse, @@ -18,8 +19,12 @@ fn main() { remove_schemas(&out_dir).unwrap(); export_schema(&schema_for!(InstantiateMsg), &out_dir); - export_schema_with_title(&schema_for!(ExecuteMsg), &out_dir, "ExecuteMsg"); - export_schema(&schema_for!(QueryMsg), &out_dir); + export_schema_with_title( + &schema_for!(ExecuteMsg), + &out_dir, + "ExecuteMsg", + ); + export_schema(&schema_for!(QueryMsg), &out_dir); export_schema_with_title( &schema_for!(AllNftInfoResponse), &out_dir, diff --git a/contracts/cw2981-royalties/schema/cw2981_query_msg.json b/contracts/cw2981-royalties/schema/cw2981_query_msg.json index 083b2806d..cb01c0bd7 100644 --- a/contracts/cw2981-royalties/schema/cw2981_query_msg.json +++ b/contracts/cw2981-royalties/schema/cw2981_query_msg.json @@ -39,215 +39,6 @@ } }, "additionalProperties": false - }, - { - "description": "Return the owner of the given token, error if token does not exist Return type: OwnerOfResponse", - "type": "object", - "required": [ - "owner_of" - ], - "properties": { - "owner_of": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - } - } - }, - "additionalProperties": false - }, - { - "description": "List all operators that can access all of the owner's tokens. Return type: `OperatorsResponse`", - "type": "object", - "required": [ - "all_operators" - ], - "properties": { - "all_operators": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, 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 - }, - { - "description": "Total number of tokens issued", - "type": "object", - "required": [ - "num_tokens" - ], - "properties": { - "num_tokens": { - "type": "object" - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns top-level metadata about the contract: `ContractInfoResponse`", - "type": "object", - "required": [ - "contract_info" - ], - "properties": { - "contract_info": { - "type": "object" - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* but directly from the contract: `NftInfoResponse`", - "type": "object", - "required": [ - "nft_info" - ], - "properties": { - "nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "token_id": { - "type": "string" - } - } - } - }, - "additionalProperties": false - }, - { - "description": "With MetaData Extension. Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization for clients: `AllNftInfo`", - "type": "object", - "required": [ - "all_nft_info" - ], - "properties": { - "all_nft_info": { - "type": "object", - "required": [ - "token_id" - ], - "properties": { - "include_expired": { - "description": "unset or false will filter out expired approvals, you must set to true to see them", - "type": [ - "boolean", - "null" - ] - }, - "token_id": { - "type": "string" - } - } - } - }, - "additionalProperties": false - }, - { - "description": "With Enumerable extension. Returns all tokens owned by the given address, [] if unset. Return type: TokensResponse.", - "type": "object", - "required": [ - "tokens" - ], - "properties": { - "tokens": { - "type": "object", - "required": [ - "owner" - ], - "properties": { - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "owner": { - "type": "string" - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - } - } - }, - "additionalProperties": false - }, - { - "description": "With Enumerable extension. Requires pagination. Lists all token_ids controlled by the contract. Return type: TokensResponse.", - "type": "object", - "required": [ - "all_tokens" - ], - "properties": { - "all_tokens": { - "type": "object", - "properties": { - "limit": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0.0 - }, - "start_after": { - "type": [ - "string", - "null" - ] - } - } - } - }, - "additionalProperties": false } ], "definitions": { diff --git a/contracts/cw2981-royalties/schema/execute_msg.json b/contracts/cw2981-royalties/schema/execute_msg.json index 5cdf6603f..2c5662c01 100644 --- a/contracts/cw2981-royalties/schema/execute_msg.json +++ b/contracts/cw2981-royalties/schema/execute_msg.json @@ -202,6 +202,27 @@ } }, "additionalProperties": false + }, + { + "description": "Extension msg", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + } + } + }, + "additionalProperties": false } ], "definitions": { diff --git a/contracts/cw721-metadata-onchain/schema/query_msg.json b/contracts/cw2981-royalties/schema/query_msg_for__cw2981_query_msg.json similarity index 74% rename from contracts/cw721-metadata-onchain/schema/query_msg.json rename to contracts/cw2981-royalties/schema/query_msg_for__cw2981_query_msg.json index cd3a95619..d2c08406c 100644 --- a/contracts/cw721-metadata-onchain/schema/query_msg.json +++ b/contracts/cw2981-royalties/schema/query_msg_for__cw2981_query_msg.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", + "title": "QueryMsg_for_Cw2981QueryMsg", "oneOf": [ { "description": "Return the owner of the given token, error if token does not exist Return type: OwnerOfResponse", @@ -280,6 +280,75 @@ } }, "additionalProperties": false + }, + { + "description": "Extension query", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Cw2981QueryMsg" + } + } + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Cw2981QueryMsg": { + "oneOf": [ + { + "description": "Should be called on sale to see if royalties are owed by the marketplace selling the NFT, if CheckRoyalties returns true See https://eips.ethereum.org/EIPS/eip-2981", + "type": "object", + "required": [ + "royalty_info" + ], + "properties": { + "royalty_info": { + "type": "object", + "required": [ + "sale_price", + "token_id" + ], + "properties": { + "sale_price": { + "$ref": "#/definitions/Uint128" + }, + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Called against contract to determine if this NFT implements royalties. Should return a boolean as part of CheckRoyaltiesResponse - default can simply be true if royalties are implemented at token level (i.e. always check on sale)", + "type": "object", + "required": [ + "check_royalties" + ], + "properties": { + "check_royalties": { + "type": "object" + } + }, + "additionalProperties": false + } + ] + }, + "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" } - ] + } } diff --git a/contracts/cw2981-royalties/src/lib.rs b/contracts/cw2981-royalties/src/lib.rs index 370b566e6..0f0788bea 100644 --- a/contracts/cw2981-royalties/src/lib.rs +++ b/contracts/cw2981-royalties/src/lib.rs @@ -48,8 +48,9 @@ pub type Extension = Option; pub type MintExtension = Option; -pub type Cw2981Contract<'a> = Cw721Contract<'a, Extension, Empty>; -pub type ExecuteMsg = cw721_base::ExecuteMsg; +pub type Cw2981Contract<'a> = Cw721Contract<'a, Extension, Empty, Empty, Cw2981QueryMsg>; +pub type ExecuteMsg = cw721_base::ExecuteMsg; +pub type QueryMsg = cw721_base::QueryMsg; #[cfg(not(feature = "library"))] pub mod entry { @@ -83,14 +84,16 @@ pub mod entry { } #[entry_point] - pub fn query(deps: Deps, env: Env, msg: Cw2981QueryMsg) -> StdResult { + pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - Cw2981QueryMsg::RoyaltyInfo { - token_id, - sale_price, - } => to_binary(&query_royalties_info(deps, token_id, sale_price)?), - Cw2981QueryMsg::CheckRoyalties {} => to_binary(&check_royalties(deps)?), - _ => Cw2981Contract::default().query(deps, env, msg.into()), + QueryMsg::Extension { msg } => match msg { + Cw2981QueryMsg::RoyaltyInfo { + token_id, + sale_price, + } => to_binary(&query_royalties_info(deps, token_id, sale_price)?), + Cw2981QueryMsg::CheckRoyalties {} => to_binary(&check_royalties(deps)?), + }, + _ => Cw2981Contract::default().query(deps, env, msg), } } } @@ -173,7 +176,9 @@ mod tests { assert_eq!(res, expected); // also check the longhand way - let query_msg = Cw2981QueryMsg::CheckRoyalties {}; + let query_msg = QueryMsg::Extension { + msg: Cw2981QueryMsg::CheckRoyalties {}, + }; let query_res: CheckRoyaltiesResponse = from_binary(&entry::query(deps.as_ref(), mock_env(), query_msg).unwrap()).unwrap(); assert_eq!(query_res, expected); @@ -216,9 +221,11 @@ mod tests { assert_eq!(res, expected); // also check the longhand way - let query_msg = Cw2981QueryMsg::RoyaltyInfo { - token_id: token_id.to_string(), - sale_price: Uint128::new(100), + let query_msg = QueryMsg::Extension { + msg: Cw2981QueryMsg::RoyaltyInfo { + token_id: token_id.to_string(), + sale_price: Uint128::new(100), + }, }; let query_res: RoyaltiesInfoResponse = from_binary(&entry::query(deps.as_ref(), mock_env(), query_msg).unwrap()).unwrap(); diff --git a/contracts/cw2981-royalties/src/msg.rs b/contracts/cw2981-royalties/src/msg.rs index f33ea6bf2..7c5469292 100644 --- a/contracts/cw2981-royalties/src/msg.rs +++ b/contracts/cw2981-royalties/src/msg.rs @@ -1,9 +1,8 @@ use cosmwasm_std::Uint128; +use cw721::CustomMsg; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cw721_base::msg::QueryMsg as CW721QueryMsg; - #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] #[serde(rename_all = "snake_case")] pub enum Cw2981QueryMsg { @@ -25,106 +24,16 @@ pub enum Cw2981QueryMsg { /// if royalties are implemented at token level /// (i.e. always check on sale) CheckRoyalties {}, - /// Return the owner of the given token, error if token does not exist - /// Return type: OwnerOfResponse - OwnerOf { - token_id: String, - /// unset or false will filter out expired approvals, you must set to true to see them - include_expired: Option, - }, - /// List all operators that can access all of the owner's tokens. - /// Return type: `OperatorsResponse` - AllOperators { - owner: String, - /// unset or false will filter out expired approvals, you must set to true to see them - include_expired: Option, - start_after: Option, - limit: Option, - }, - /// Total number of tokens issued - NumTokens {}, - - /// With MetaData Extension. - /// Returns top-level metadata about the contract: `ContractInfoResponse` - ContractInfo {}, - /// With MetaData Extension. - /// Returns metadata about one particular token, based on *ERC721 Metadata JSON Schema* - /// but directly from the contract: `NftInfoResponse` - NftInfo { token_id: String }, - /// With MetaData Extension. - /// Returns the result of both `NftInfo` and `OwnerOf` as one query as an optimization - /// for clients: `AllNftInfo` - AllNftInfo { - token_id: String, - /// unset or false will filter out expired approvals, you must set to true to see them - include_expired: Option, - }, - - /// With Enumerable extension. - /// Returns all tokens owned by the given address, [] if unset. - /// Return type: TokensResponse. - Tokens { - owner: String, - start_after: Option, - limit: Option, - }, - /// With Enumerable extension. - /// Requires pagination. Lists all token_ids controlled by the contract. - /// Return type: TokensResponse. - AllTokens { - start_after: Option, - limit: Option, - }, } -impl From for CW721QueryMsg { - fn from(msg: Cw2981QueryMsg) -> CW721QueryMsg { - match msg { - Cw2981QueryMsg::OwnerOf { - token_id, - include_expired, - } => CW721QueryMsg::OwnerOf { - token_id, - include_expired, - }, - Cw2981QueryMsg::AllOperators { - owner, - include_expired, - start_after, - limit, - } => CW721QueryMsg::AllOperators { - owner, - include_expired, - start_after, - limit, - }, - Cw2981QueryMsg::NumTokens {} => CW721QueryMsg::NumTokens {}, - Cw2981QueryMsg::ContractInfo {} => CW721QueryMsg::ContractInfo {}, - Cw2981QueryMsg::NftInfo { token_id } => CW721QueryMsg::NftInfo { token_id }, - Cw2981QueryMsg::AllNftInfo { - token_id, - include_expired, - } => CW721QueryMsg::AllNftInfo { - token_id, - include_expired, - }, - Cw2981QueryMsg::Tokens { - owner, - start_after, - limit, - } => CW721QueryMsg::Tokens { - owner, - start_after, - limit, - }, - Cw2981QueryMsg::AllTokens { start_after, limit } => { - CW721QueryMsg::AllTokens { start_after, limit } - } - _ => panic!("cannot covert {:?} to CW721QueryMsg", msg), - } +impl Default for Cw2981QueryMsg { + fn default() -> Self { + Cw2981QueryMsg::CheckRoyalties {} } } +impl CustomMsg for Cw2981QueryMsg {} + #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] pub struct RoyaltiesInfoResponse { pub address: String, diff --git a/contracts/cw721-base/examples/schema.rs b/contracts/cw721-base/examples/schema.rs index c315be662..ccb8e376f 100644 --- a/contracts/cw721-base/examples/schema.rs +++ b/contracts/cw721-base/examples/schema.rs @@ -2,6 +2,7 @@ use std::env::current_dir; use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for}; +use cosmwasm_std::Empty; use cw721::{ AllNftInfoResponse, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, NftInfoResponse, @@ -16,8 +17,12 @@ fn main() { remove_schemas(&out_dir).unwrap(); export_schema(&schema_for!(InstantiateMsg), &out_dir); - export_schema_with_title(&schema_for!(ExecuteMsg), &out_dir, "ExecuteMsg"); - export_schema(&schema_for!(QueryMsg), &out_dir); + export_schema_with_title( + &schema_for!(ExecuteMsg), + &out_dir, + "ExecuteMsg", + ); + export_schema(&schema_for!(QueryMsg), &out_dir); export_schema_with_title( &schema_for!(AllNftInfoResponse), &out_dir, diff --git a/contracts/cw721-base/schema/execute_msg.json b/contracts/cw721-base/schema/execute_msg.json index 5cdf6603f..2c5662c01 100644 --- a/contracts/cw721-base/schema/execute_msg.json +++ b/contracts/cw721-base/schema/execute_msg.json @@ -202,6 +202,27 @@ } }, "additionalProperties": false + }, + { + "description": "Extension msg", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + } + } + }, + "additionalProperties": false } ], "definitions": { diff --git a/contracts/cw2981-royalties/schema/query_msg.json b/contracts/cw721-base/schema/query_msg_for__empty.json similarity index 88% rename from contracts/cw2981-royalties/schema/query_msg.json rename to contracts/cw721-base/schema/query_msg_for__empty.json index cd3a95619..68862e10f 100644 --- a/contracts/cw2981-royalties/schema/query_msg.json +++ b/contracts/cw721-base/schema/query_msg_for__empty.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", + "title": "QueryMsg_for_Empty", "oneOf": [ { "description": "Return the owner of the given token, error if token does not exist Return type: OwnerOfResponse", @@ -280,6 +280,33 @@ } }, "additionalProperties": false + }, + { + "description": "Extension query", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + } + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" } - ] + } } diff --git a/contracts/cw721-base/src/contract_tests.rs b/contracts/cw721-base/src/contract_tests.rs index c578921b0..315229ace 100644 --- a/contracts/cw721-base/src/contract_tests.rs +++ b/contracts/cw721-base/src/contract_tests.rs @@ -15,7 +15,7 @@ const MINTER: &str = "merlin"; const CONTRACT_NAME: &str = "Magic Power"; const SYMBOL: &str = "MGK"; -fn setup_contract(deps: DepsMut<'_>) -> Cw721Contract<'static, Extension, Empty> { +fn setup_contract(deps: DepsMut<'_>) -> Cw721Contract<'static, Extension, Empty, Empty, Empty> { let contract = Cw721Contract::default(); let msg = InstantiateMsg { name: CONTRACT_NAME.to_string(), @@ -31,7 +31,7 @@ fn setup_contract(deps: DepsMut<'_>) -> Cw721Contract<'static, Extension, Empty> #[test] fn proper_instantiation() { let mut deps = mock_dependencies(); - let contract = Cw721Contract::::default(); + let contract = Cw721Contract::::default(); let msg = InstantiateMsg { name: CONTRACT_NAME.to_string(), diff --git a/contracts/cw721-base/src/execute.rs b/contracts/cw721-base/src/execute.rs index 6176a5d90..2fb9aebd3 100644 --- a/contracts/cw721-base/src/execute.rs +++ b/contracts/cw721-base/src/execute.rs @@ -14,10 +14,12 @@ use crate::state::{Approval, Cw721Contract, TokenInfo}; const CONTRACT_NAME: &str = "crates.io:cw721-base"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -impl<'a, T, C> Cw721Contract<'a, T, C> +impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> where T: Serialize + DeserializeOwned + Clone, C: CustomMsg, + E: CustomMsg, + Q: CustomMsg, { pub fn instantiate( &self, @@ -43,7 +45,7 @@ where deps: DepsMut, env: Env, info: MessageInfo, - msg: ExecuteMsg, + msg: ExecuteMsg, ) -> Result, ContractError> { match msg { ExecuteMsg::Mint(msg) => self.mint(deps, env, info, msg), @@ -69,15 +71,18 @@ where msg, } => self.send_nft(deps, env, info, contract, token_id, msg), ExecuteMsg::Burn { token_id } => self.burn(deps, env, info, token_id), + ExecuteMsg::Extension { msg: _ } => Ok(Response::default()), } } } // TODO pull this into some sort of trait extension?? -impl<'a, T, C> Cw721Contract<'a, T, C> +impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> where T: Serialize + DeserializeOwned + Clone, C: CustomMsg, + E: CustomMsg, + Q: CustomMsg, { pub fn mint( &self, @@ -115,10 +120,12 @@ where } } -impl<'a, T, C> Cw721Execute for Cw721Contract<'a, T, C> +impl<'a, T, C, E, Q> Cw721Execute for Cw721Contract<'a, T, C, E, Q> where T: Serialize + DeserializeOwned + Clone, C: CustomMsg, + E: CustomMsg, + Q: CustomMsg, { type Err = ContractError; @@ -264,10 +271,12 @@ where } // helpers -impl<'a, T, C> Cw721Contract<'a, T, C> +impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> where T: Serialize + DeserializeOwned + Clone, C: CustomMsg, + E: CustomMsg, + Q: CustomMsg, { pub fn _transfer_nft( &self, diff --git a/contracts/cw721-base/src/helpers.rs b/contracts/cw721-base/src/helpers.rs index 437132f5d..0d590a7bb 100644 --- a/contracts/cw721-base/src/helpers.rs +++ b/contracts/cw721-base/src/helpers.rs @@ -1,22 +1,29 @@ use crate::{ExecuteMsg, QueryMsg}; -use cosmwasm_std::{to_binary, Addr, CosmosMsg, QuerierWrapper, StdResult, WasmMsg, WasmQuery}; +use cosmwasm_std::{ + to_binary, Addr, CosmosMsg, CustomMsg, QuerierWrapper, StdResult, WasmMsg, WasmQuery, +}; use cw721::{ AllNftInfoResponse, Approval, ApprovalResponse, ApprovalsResponse, ContractInfoResponse, NftInfoResponse, NumTokensResponse, OperatorsResponse, OwnerOfResponse, TokensResponse, }; +use serde::__private::PhantomData; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct Cw721Contract(pub Addr); +pub struct Cw721Contract( + pub Addr, + pub PhantomData, + pub PhantomData, +); #[allow(dead_code)] -impl Cw721Contract { +impl Cw721Contract { pub fn addr(&self) -> Addr { self.0.clone() } - pub fn call(&self, msg: ExecuteMsg) -> StdResult { + pub fn call(&self, msg: ExecuteMsg) -> StdResult { let msg = to_binary(&msg)?; Ok(WasmMsg::Execute { contract_addr: self.addr().into(), @@ -29,7 +36,7 @@ impl Cw721Contract { pub fn query( &self, querier: &QuerierWrapper, - req: QueryMsg, + req: QueryMsg, ) -> StdResult { let query = WasmQuery::Smart { contract_addr: self.addr().into(), diff --git a/contracts/cw721-base/src/lib.rs b/contracts/cw721-base/src/lib.rs index 3a83743f5..71e8acd31 100644 --- a/contracts/cw721-base/src/lib.rs +++ b/contracts/cw721-base/src/lib.rs @@ -29,7 +29,7 @@ pub mod entry { info: MessageInfo, msg: InstantiateMsg, ) -> StdResult { - let tract = Cw721Contract::::default(); + let tract = Cw721Contract::::default(); tract.instantiate(deps, env, info, msg) } @@ -38,15 +38,15 @@ pub mod entry { deps: DepsMut, env: Env, info: MessageInfo, - msg: ExecuteMsg, + msg: ExecuteMsg, ) -> Result { - let tract = Cw721Contract::::default(); + let tract = Cw721Contract::::default(); tract.execute(deps, env, info, msg) } #[entry_point] - pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { - let tract = Cw721Contract::::default(); + pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + let tract = Cw721Contract::::default(); tract.query(deps, env, msg) } } diff --git a/contracts/cw721-base/src/msg.rs b/contracts/cw721-base/src/msg.rs index 7b3540d8c..f5abc0c59 100644 --- a/contracts/cw721-base/src/msg.rs +++ b/contracts/cw721-base/src/msg.rs @@ -22,7 +22,7 @@ pub struct InstantiateMsg { /// use other control logic in any contract that inherits this. #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] #[serde(rename_all = "snake_case")] -pub enum ExecuteMsg { +pub enum ExecuteMsg { /// Transfer is a base message to move a token to another account without triggering actions TransferNft { recipient: String, token_id: String }, /// Send is a base message to transfer a token to a contract and trigger an action @@ -55,6 +55,9 @@ pub enum ExecuteMsg { /// Burn an NFT the sender has access to Burn { token_id: String }, + + /// Extension msg + Extension { msg: E }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -73,7 +76,7 @@ pub struct MintMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] -pub enum QueryMsg { +pub enum QueryMsg { /// Return the owner of the given token, error if token does not exist /// Return type: OwnerOfResponse OwnerOf { @@ -142,6 +145,11 @@ pub enum QueryMsg { // Return the minter Minter {}, + + /// Extension query + Extension { + msg: Q, + }, } /// Shows who can mint these tokens diff --git a/contracts/cw721-base/src/query.rs b/contracts/cw721-base/src/query.rs index 162aaf038..f71d5d9e0 100644 --- a/contracts/cw721-base/src/query.rs +++ b/contracts/cw721-base/src/query.rs @@ -17,10 +17,12 @@ use crate::state::{Approval, Cw721Contract, TokenInfo}; const DEFAULT_LIMIT: u32 = 10; const MAX_LIMIT: u32 = 100; -impl<'a, T, C> Cw721Query for Cw721Contract<'a, T, C> +impl<'a, T, C, E, Q> Cw721Query for Cw721Contract<'a, T, C, E, Q> where T: Serialize + DeserializeOwned + Clone, C: CustomMsg, + E: CustomMsg, + Q: CustomMsg, { fn contract_info(&self, deps: Deps) -> StdResult { self.contract_info.load(deps.storage) @@ -205,10 +207,12 @@ where } } -impl<'a, T, C> Cw721Contract<'a, T, C> +impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> where T: Serialize + DeserializeOwned + Clone, C: CustomMsg, + E: CustomMsg, + Q: CustomMsg, { pub fn minter(&self, deps: Deps) -> StdResult { let minter_addr = self.minter.load(deps.storage)?; @@ -217,7 +221,7 @@ where }) } - pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::Minter {} => to_binary(&self.minter(deps)?), QueryMsg::ContractInfo {} => to_binary(&self.contract_info(deps)?), @@ -276,6 +280,7 @@ where } => { to_binary(&self.approvals(deps, env, token_id, include_expired.unwrap_or(false))?) } + QueryMsg::Extension { msg: _ } => Ok(Binary::default()), } } } diff --git a/contracts/cw721-base/src/state.rs b/contracts/cw721-base/src/state.rs index c411a42c3..8c23a4f9d 100644 --- a/contracts/cw721-base/src/state.rs +++ b/contracts/cw721-base/src/state.rs @@ -8,9 +8,11 @@ use cosmwasm_std::{Addr, BlockInfo, StdResult, Storage}; use cw721::{ContractInfoResponse, CustomMsg, Cw721, Expiration}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; -pub struct Cw721Contract<'a, T, C> +pub struct Cw721Contract<'a, T, C, E, Q> where T: Serialize + DeserializeOwned + Clone, + Q: CustomMsg, + E: CustomMsg, { pub contract_info: Item<'a, ContractInfoResponse>, pub minter: Item<'a, Addr>, @@ -20,19 +22,25 @@ where pub tokens: IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a, T>>, pub(crate) _custom_response: PhantomData, + pub(crate) _custom_query: PhantomData, + pub(crate) _custom_execute: PhantomData, } // This is a signal, the implementations are in other files -impl<'a, T, C> Cw721 for Cw721Contract<'a, T, C> +impl<'a, T, C, E, Q> Cw721 for Cw721Contract<'a, T, C, E, Q> where T: Serialize + DeserializeOwned + Clone, C: CustomMsg, + E: CustomMsg, + Q: CustomMsg, { } -impl Default for Cw721Contract<'static, T, C> +impl Default for Cw721Contract<'static, T, C, E, Q> where T: Serialize + DeserializeOwned + Clone, + E: CustomMsg, + Q: CustomMsg, { fn default() -> Self { Self::new( @@ -46,9 +54,11 @@ where } } -impl<'a, T, C> Cw721Contract<'a, T, C> +impl<'a, T, C, E, Q> Cw721Contract<'a, T, C, E, Q> where T: Serialize + DeserializeOwned + Clone, + E: CustomMsg, + Q: CustomMsg, { fn new( contract_key: &'a str, @@ -68,6 +78,8 @@ where operators: Map::new(operator_key), tokens: IndexedMap::new(tokens_key, indexes), _custom_response: PhantomData, + _custom_execute: PhantomData, + _custom_query: PhantomData, } } diff --git a/contracts/cw721-fixed-price/src/contract.rs b/contracts/cw721-fixed-price/src/contract.rs index 20f00bd5d..ee82fb58e 100644 --- a/contracts/cw721-fixed-price/src/contract.rs +++ b/contracts/cw721-fixed-price/src/contract.rs @@ -1,11 +1,13 @@ +use std::marker::PhantomData; + use crate::error::ContractError; use crate::msg::{ConfigResponse, ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{Config, CONFIG}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, ReplyOn, Response, StdResult, - SubMsg, Uint128, WasmMsg, + to_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, ReplyOn, Response, + StdResult, SubMsg, Uint128, WasmMsg, }; use cw2::set_contract_version; use cw20::Cw20ReceiveMsg; @@ -157,7 +159,7 @@ pub fn execute_receive( return Err(ContractError::WrongPaymentAmount {}); } - let mint_msg = Cw721ExecuteMsg::Mint(MintMsg:: { + let mint_msg = Cw721ExecuteMsg::::Mint(MintMsg:: { token_id: config.unused_token_id.to_string(), owner: sender, token_uri: config.token_uri.clone().into(), @@ -166,7 +168,8 @@ pub fn execute_receive( match config.cw721_address.clone() { Some(cw721) => { - let callback = Cw721Contract(cw721).call(mint_msg)?; + let callback = + Cw721Contract::(cw721, PhantomData, PhantomData).call(mint_msg)?; config.unused_token_id += 1; CONFIG.save(deps.storage, &config)?; @@ -368,7 +371,7 @@ mod tests { let info = mock_info(MOCK_CONTRACT_ADDR, &[]); let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - let mint_msg = Cw721ExecuteMsg::Mint(MintMsg:: { + let mint_msg = Cw721ExecuteMsg::::Mint(MintMsg:: { token_id: String::from("0"), owner: String::from("minter"), token_uri: Some(String::from("https://ipfs.io/ipfs/Q")), diff --git a/contracts/cw721-metadata-onchain/schema/execute_msg.json b/contracts/cw721-metadata-onchain/schema/execute_msg.json index 58e2593fd..0e418ae2a 100644 --- a/contracts/cw721-metadata-onchain/schema/execute_msg.json +++ b/contracts/cw721-metadata-onchain/schema/execute_msg.json @@ -202,6 +202,27 @@ } }, "additionalProperties": false + }, + { + "description": "Extension msg", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + } + } + }, + "additionalProperties": false } ], "definitions": { @@ -209,6 +230,10 @@ "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", "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": [ diff --git a/contracts/cw721-base/schema/query_msg.json b/contracts/cw721-metadata-onchain/schema/query_msg_for__empty.json similarity index 88% rename from contracts/cw721-base/schema/query_msg.json rename to contracts/cw721-metadata-onchain/schema/query_msg_for__empty.json index cd3a95619..68862e10f 100644 --- a/contracts/cw721-base/schema/query_msg.json +++ b/contracts/cw721-metadata-onchain/schema/query_msg_for__empty.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", + "title": "QueryMsg_for_Empty", "oneOf": [ { "description": "Return the owner of the given token, error if token does not exist Return type: OwnerOfResponse", @@ -280,6 +280,33 @@ } }, "additionalProperties": false + }, + { + "description": "Extension query", + "type": "object", + "required": [ + "extension" + ], + "properties": { + "extension": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "$ref": "#/definitions/Empty" + } + } + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" } - ] + } } diff --git a/contracts/cw721-metadata-onchain/src/lib.rs b/contracts/cw721-metadata-onchain/src/lib.rs index 560e36ed3..4f0728adf 100644 --- a/contracts/cw721-metadata-onchain/src/lib.rs +++ b/contracts/cw721-metadata-onchain/src/lib.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use cosmwasm_std::Empty; use cw2::set_contract_version; -pub use cw721_base::{ContractError, InstantiateMsg, MintMsg, MinterResponse, QueryMsg}; +pub use cw721_base::{ContractError, InstantiateMsg, MintMsg, MinterResponse}; // Version info for migration const CONTRACT_NAME: &str = "crates.io:cw721-metadata-onchain"; @@ -32,8 +32,9 @@ pub struct Metadata { pub type Extension = Option; -pub type Cw721MetadataContract<'a> = cw721_base::Cw721Contract<'a, Extension, Empty>; -pub type ExecuteMsg = cw721_base::ExecuteMsg; +pub type Cw721MetadataContract<'a> = cw721_base::Cw721Contract<'a, Extension, Empty, Empty, Empty>; +pub type ExecuteMsg = cw721_base::ExecuteMsg; +pub type QueryMsg = cw721_base::QueryMsg; #[cfg(not(feature = "library"))] pub mod entry {