From 15d299031497f4026fcdf0c3a067be9418f41e94 Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Thu, 25 Jan 2024 18:22:30 +0200 Subject: [PATCH] Open Edition minter update & tests (#629) * Add the option to limit number of tokens instead of end time * Make linter happy * Update schema files --- .../schema/instantiate_msg.json | 8 +- .../open-edition-factory/src/contract.rs | 5 + .../open-edition-factory/src/error.rs | 3 + .../factories/open-edition-factory/src/msg.rs | 31 +++- .../open-edition-factory/src/state.rs | 1 + .../schema/config_response.json | 18 ++- .../schema/execute_msg.json | 13 ++ .../schema/instantiate_msg.json | 26 ++- .../schema/mintable_num_tokens_response.json | 8 +- .../minter_config_for__config_extension.json | 18 ++- .../open-edition-minter/schema/query_msg.json | 13 ++ .../open-edition-minter/src/contract.rs | 149 ++++++++++++++---- .../minters/open-edition-minter/src/error.rs | 6 + .../minters/open-edition-minter/src/msg.rs | 9 +- .../minters/open-edition-minter/src/state.rs | 5 +- .../helpers/open_edition_minter_helpers.rs | 5 +- .../open_edition_factory_and_mint_tests.rs | 10 +- .../open_edition_minter_executes_tests.rs | 3 +- test-suite/src/common_setup/msg.rs | 2 + .../open_edition_minter/minter_params.rs | 10 +- .../open_edition_minter/mock_params.rs | 11 +- .../setup_minter/open_edition_minter/setup.rs | 7 + test-suite/src/common_setup/templates.rs | 30 +++- .../tests/integration_tests.rs | 5 +- .../src/open_edition_factory/tests/queries.rs | 4 + .../open_edition_factory/tests/sudo_tests.rs | 9 +- test-suite/src/open_edition_minter/tests.rs | 1 + .../tests/address_limit.rs | 6 +- .../tests/allowed_code_ids.rs | 6 +- .../complete_mint_all_outcomes_validation.rs | 114 ++++++++++++-- .../tests/factory_create_minter.rs | 87 +++++++++- .../tests/frozen_factory.rs | 12 +- .../tests/ibc_asset_mint.rs | 11 +- .../tests/max_tokens_limit.rs | 92 +++++++++++ .../tests/update_mint_price.rs | 4 +- .../tests/update_start_and_end_time.rs | 6 +- 36 files changed, 651 insertions(+), 97 deletions(-) create mode 100644 test-suite/src/open_edition_minter/tests/max_tokens_limit.rs diff --git a/contracts/factories/open-edition-factory/schema/instantiate_msg.json b/contracts/factories/open-edition-factory/schema/instantiate_msg.json index 9c9ec2890..1a0956924 100644 --- a/contracts/factories/open-edition-factory/schema/instantiate_msg.json +++ b/contracts/factories/open-edition-factory/schema/instantiate_msg.json @@ -86,7 +86,8 @@ "airdrop_mint_fee_bps", "airdrop_mint_price", "dev_fee_address", - "max_per_address_limit" + "max_per_address_limit", + "max_token_limit" ], "properties": { "airdrop_mint_fee_bps": { @@ -104,6 +105,11 @@ "type": "integer", "format": "uint32", "minimum": 0.0 + }, + "max_token_limit": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 } }, "additionalProperties": false diff --git a/contracts/factories/open-edition-factory/src/contract.rs b/contracts/factories/open-edition-factory/src/contract.rs index 7021ed302..5be395cfe 100644 --- a/contracts/factories/open-edition-factory/src/contract.rs +++ b/contracts/factories/open-edition-factory/src/contract.rs @@ -124,6 +124,11 @@ pub fn sudo_update_params( update_params(&mut params, param_msg.clone())?; + params.extension.max_token_limit = param_msg + .extension + .max_token_limit + .unwrap_or(params.extension.max_token_limit); + params.extension.dev_fee_address = param_msg .extension .dev_fee_address diff --git a/contracts/factories/open-edition-factory/src/error.rs b/contracts/factories/open-edition-factory/src/error.rs index 2e15e0d5c..cbf12faff 100644 --- a/contracts/factories/open-edition-factory/src/error.rs +++ b/contracts/factories/open-edition-factory/src/error.rs @@ -25,6 +25,9 @@ pub enum ContractError { #[error("Unauthorized")] Unauthorized {}, + #[error("LimitOfTimeOrNumTokensRequired")] + LimitOfTimeOrNumTokensRequired {}, + #[error("InvalidMintPrice")] InvalidMintPrice {}, diff --git a/contracts/factories/open-edition-factory/src/msg.rs b/contracts/factories/open-edition-factory/src/msg.rs index 825123178..751047649 100644 --- a/contracts/factories/open-edition-factory/src/msg.rs +++ b/contracts/factories/open-edition-factory/src/msg.rs @@ -16,9 +16,10 @@ pub struct InstantiateMsg { pub struct OpenEditionMinterInitMsgExtension { pub nft_data: NftData, pub start_time: Timestamp, - pub end_time: Timestamp, + pub end_time: Option, pub mint_price: Coin, pub per_address_limit: u32, + pub num_tokens: Option, // If not the admin/init pub payment_address: Option, } @@ -33,6 +34,16 @@ impl OpenEditionMinterInitMsgExtension { // Validation of the Minter Params -> need to be in-line with the factory init_msg.nft_data = NftData::validate(init_msg.nft_data)?; + // Optional: can have a max mint amount + if let Some(max_num_tokens) = init_msg.num_tokens { + if max_num_tokens == 0 || max_num_tokens > params.extension.max_token_limit { + return Err(ContractError::InvalidNumTokens { + min: 1, + max: params.extension.max_token_limit, + }); + } + } + let max = params.extension.max_per_address_limit; let min = 1; let per_address_limit = init_msg.per_address_limit; @@ -51,11 +62,17 @@ impl OpenEditionMinterInitMsgExtension { )); } - if init_msg.end_time <= init_msg.start_time { - return Err(ContractError::InvalidEndTime( - init_msg.start_time, - init_msg.end_time, - )); + // Optional: not time limited + if let Some(end_time) = init_msg.end_time { + if end_time <= init_msg.start_time { + return Err(ContractError::InvalidEndTime(init_msg.start_time, end_time)); + } + } + + // Need to validate the end time and number of tokens are not both None + // At least 1 constraint is required + if init_msg.end_time.is_none() && init_msg.num_tokens.is_none() { + return Err(ContractError::LimitOfTimeOrNumTokensRequired {}); } if init_msg.mint_price.amount < params.min_mint_price.amount { @@ -68,6 +85,7 @@ impl OpenEditionMinterInitMsgExtension { end_time: init_msg.end_time, mint_price: init_msg.mint_price, per_address_limit, + num_tokens: init_msg.num_tokens, payment_address: init_msg.payment_address, }) } @@ -85,6 +103,7 @@ pub enum SudoMsg { /// Message for params so they can be updated individually by governance #[cw_serde] pub struct OpenEditionUpdateParamsExtension { + pub max_token_limit: Option, pub max_per_address_limit: Option, pub min_mint_price: Option, pub airdrop_mint_fee_bps: Option, diff --git a/contracts/factories/open-edition-factory/src/state.rs b/contracts/factories/open-edition-factory/src/state.rs index 1a2daa3f9..9e60a8950 100644 --- a/contracts/factories/open-edition-factory/src/state.rs +++ b/contracts/factories/open-edition-factory/src/state.rs @@ -6,6 +6,7 @@ use sg2::MinterParams; #[cw_serde] pub struct ParamsExtension { + pub max_token_limit: u32, pub max_per_address_limit: u32, pub airdrop_mint_fee_bps: u64, pub airdrop_mint_price: Coin, diff --git a/contracts/minters/open-edition-minter/schema/config_response.json b/contracts/minters/open-edition-minter/schema/config_response.json index e21e6fc5b..6140d7629 100644 --- a/contracts/minters/open-edition-minter/schema/config_response.json +++ b/contracts/minters/open-edition-minter/schema/config_response.json @@ -4,7 +4,6 @@ "type": "object", "required": [ "admin", - "end_time", "factory", "mint_price", "nft_data", @@ -18,7 +17,14 @@ "type": "string" }, "end_time": { - "$ref": "#/definitions/Timestamp" + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] }, "factory": { "type": "string" @@ -29,6 +35,14 @@ "nft_data": { "$ref": "#/definitions/NftData" }, + "num_tokens": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, "payment_address": { "anyOf": [ { diff --git a/contracts/minters/open-edition-minter/schema/execute_msg.json b/contracts/minters/open-edition-minter/schema/execute_msg.json index 704417ac7..51a2ffa81 100644 --- a/contracts/minters/open-edition-minter/schema/execute_msg.json +++ b/contracts/minters/open-edition-minter/schema/execute_msg.json @@ -138,6 +138,19 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "burn_remaining" + ], + "properties": { + "burn_remaining": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false } ], "definitions": { diff --git a/contracts/minters/open-edition-minter/schema/instantiate_msg.json b/contracts/minters/open-edition-minter/schema/instantiate_msg.json index fd4471ee2..a7bb175db 100644 --- a/contracts/minters/open-edition-minter/schema/instantiate_msg.json +++ b/contracts/minters/open-edition-minter/schema/instantiate_msg.json @@ -294,7 +294,6 @@ "OpenEditionMinterInitMsgExtension": { "type": "object", "required": [ - "end_time", "mint_price", "nft_data", "per_address_limit", @@ -302,7 +301,14 @@ ], "properties": { "end_time": { - "$ref": "#/definitions/Timestamp" + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] }, "mint_price": { "$ref": "#/definitions/Coin" @@ -310,6 +316,14 @@ "nft_data": { "$ref": "#/definitions/NftData" }, + "num_tokens": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, "payment_address": { "type": [ "string", @@ -333,7 +347,8 @@ "airdrop_mint_fee_bps", "airdrop_mint_price", "dev_fee_address", - "max_per_address_limit" + "max_per_address_limit", + "max_token_limit" ], "properties": { "airdrop_mint_fee_bps": { @@ -351,6 +366,11 @@ "type": "integer", "format": "uint32", "minimum": 0.0 + }, + "max_token_limit": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 } }, "additionalProperties": false diff --git a/contracts/minters/open-edition-minter/schema/mintable_num_tokens_response.json b/contracts/minters/open-edition-minter/schema/mintable_num_tokens_response.json index 8b80aa85b..01e65ad4d 100644 --- a/contracts/minters/open-edition-minter/schema/mintable_num_tokens_response.json +++ b/contracts/minters/open-edition-minter/schema/mintable_num_tokens_response.json @@ -2,12 +2,12 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "MintableNumTokensResponse", "type": "object", - "required": [ - "count" - ], "properties": { "count": { - "type": "integer", + "type": [ + "integer", + "null" + ], "format": "uint32", "minimum": 0.0 } diff --git a/contracts/minters/open-edition-minter/schema/minter_config_for__config_extension.json b/contracts/minters/open-edition-minter/schema/minter_config_for__config_extension.json index 4de5745a8..3d8645596 100644 --- a/contracts/minters/open-edition-minter/schema/minter_config_for__config_extension.json +++ b/contracts/minters/open-edition-minter/schema/minter_config_for__config_extension.json @@ -50,7 +50,6 @@ "type": "object", "required": [ "admin", - "end_time", "nft_data", "per_address_limit", "start_time" @@ -60,11 +59,26 @@ "$ref": "#/definitions/Addr" }, "end_time": { - "$ref": "#/definitions/Timestamp" + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] }, "nft_data": { "$ref": "#/definitions/NftData" }, + "num_tokens": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, "payment_address": { "anyOf": [ { diff --git a/contracts/minters/open-edition-minter/schema/query_msg.json b/contracts/minters/open-edition-minter/schema/query_msg.json index f0922b7c0..0bcc3dcc0 100644 --- a/contracts/minters/open-edition-minter/schema/query_msg.json +++ b/contracts/minters/open-edition-minter/schema/query_msg.json @@ -100,6 +100,19 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "mintable_num_tokens" + ], + "properties": { + "mintable_num_tokens": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false } ] } diff --git a/contracts/minters/open-edition-minter/src/contract.rs b/contracts/minters/open-edition-minter/src/contract.rs index 09b39193c..300fb3f27 100644 --- a/contracts/minters/open-edition-minter/src/contract.rs +++ b/contracts/minters/open-edition-minter/src/contract.rs @@ -1,18 +1,18 @@ use crate::error::ContractError; use crate::helpers::mint_nft_msg; use crate::msg::{ - ConfigResponse, EndTimeResponse, ExecuteMsg, MintCountResponse, MintPriceResponse, QueryMsg, - StartTimeResponse, TotalMintCountResponse, + ConfigResponse, EndTimeResponse, ExecuteMsg, MintCountResponse, MintPriceResponse, + MintableNumTokensResponse, QueryMsg, StartTimeResponse, TotalMintCountResponse, }; use crate::state::{ - increment_token_index, Config, ConfigExtension, CONFIG, MINTER_ADDRS, SG721_ADDRESS, STATUS, - TOTAL_MINT_COUNT, + increment_token_index, Config, ConfigExtension, CONFIG, MINTABLE_NUM_TOKENS, MINTER_ADDRS, + SG721_ADDRESS, STATUS, TOTAL_MINT_COUNT, }; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - coin, to_binary, Addr, BankMsg, Binary, Coin, Decimal, Deps, DepsMut, Empty, Env, MessageInfo, - Order, Reply, ReplyOn, StdError, StdResult, Timestamp, WasmMsg, + coin, to_binary, Addr, BankMsg, Binary, Coin, Decimal, Deps, DepsMut, Empty, Env, Event, + MessageInfo, Order, Reply, ReplyOn, StdError, StdResult, Timestamp, WasmMsg, }; use cw2::set_contract_version; use cw_utils::{may_pay, maybe_addr, nonpayable, parse_reply_instantiate_data}; @@ -91,7 +91,7 @@ pub fn instantiate( } // Validations/Check at the factory level: - // - Mint price, # of tokens / address, Start & End time + // - Mint price, # of tokens / address, Start & End time, Max Tokens // Use default start trading time if not provided let mut collection_info = msg.collection_params.info.clone(); @@ -125,6 +125,7 @@ pub fn instantiate( start_time: msg.init_msg.start_time, end_time: msg.init_msg.end_time, nft_data: msg.init_msg.nft_data, + num_tokens: msg.init_msg.num_tokens, }, mint_price: msg.init_msg.mint_price, }; @@ -134,6 +135,11 @@ pub fn instantiate( // Init the minted tokens count TOTAL_MINT_COUNT.save(deps.storage, &0)?; + // Max token count (optional) + if let Some(max_num_tokens) = msg.init_msg.num_tokens { + MINTABLE_NUM_TOKENS.save(deps.storage, &max_num_tokens)?; + } + // Submessage to instantiate sg721 contract let submsg = SubMsg { msg: WasmMsg::Instantiate { @@ -182,6 +188,7 @@ pub fn execute( execute_update_per_address_limit(deps, env, info, per_address_limit) } ExecuteMsg::MintTo { recipient } => execute_mint_to(deps, env, info, recipient), + ExecuteMsg::BurnRemaining {} => execute_burn_remaining(deps, env, info), } } @@ -193,10 +200,20 @@ pub fn execute_purge( info: MessageInfo, ) -> Result { nonpayable(&info)?; - // Check if mint has ended + // check if sold out (optional) + let mintable_num_tokens = MINTABLE_NUM_TOKENS.may_load(deps.storage)?; + if let Some(mintable_nb_tokens) = mintable_num_tokens { + if mintable_nb_tokens != 0 { + return Err(ContractError::NotSoldOut {}); + } + } + + // Check if mint has ended (optional) let end_time = CONFIG.load(deps.storage)?.extension.end_time; - if env.block.time <= end_time { - return Err(ContractError::MintingHasNotYetEnded {}); + if let Some(end_time_u) = end_time { + if env.block.time <= end_time_u { + return Err(ContractError::MintingHasNotYetEnded {}); + } } let keys = MINTER_ADDRS @@ -220,12 +237,14 @@ pub fn execute_mint_sender( let config = CONFIG.load(deps.storage)?; let action = "mint_sender"; - // Check if after start_time and before end time + // Check start and end time (if not optional) if env.block.time < config.extension.start_time { return Err(ContractError::BeforeMintStartTime {}); } - if env.block.time >= config.extension.end_time { - return Err(ContractError::AfterMintEndTime {}); + if let Some(end_time) = config.extension.end_time { + if env.block.time >= end_time { + return Err(ContractError::AfterMintEndTime {}); + } } // Check if already minted max per address limit @@ -254,8 +273,10 @@ pub fn execute_mint_to( )); } - if env.block.time >= config.extension.end_time { - return Err(ContractError::AfterMintEndTime {}); + if let Some(end_time) = config.extension.end_time { + if env.block.time >= end_time { + return Err(ContractError::AfterMintEndTime {}); + } } _execute_mint(deps, env, info, action, true, Some(recipient)) @@ -272,6 +293,12 @@ fn _execute_mint( is_admin: bool, recipient: Option, ) -> Result { + let mintable_num_tokens = MINTABLE_NUM_TOKENS.may_load(deps.storage)?; + if let Some(mintable_nb_tokens) = mintable_num_tokens { + if mintable_nb_tokens == 0 { + return Err(ContractError::SoldOut {}); + } + } let config = CONFIG.load(deps.storage)?; let sg721_address = SG721_ADDRESS.load(deps.storage)?; @@ -367,6 +394,11 @@ fn _execute_mint( }, )?; + // Update mintable count (optional) + if let Some(mintable_nb_tokens) = mintable_num_tokens { + MINTABLE_NUM_TOKENS.save(deps.storage, &(mintable_nb_tokens - 1))?; + } + let seller_amount = { // the net amount is mint price - network fee (mint free + dev fee) let amount = mint_price.amount.checked_sub(network_fee)?; @@ -413,9 +445,10 @@ pub fn execute_update_mint_price( )); } - // If we are after the end_time return error - if env.block.time >= config.extension.end_time { - return Err(ContractError::AfterMintEndTime {}); + if let Some(end_time) = config.extension.end_time { + if env.block.time >= end_time { + return Err(ContractError::AfterMintEndTime {}); + } } // If current time is after the stored start_time, only allow lowering price @@ -470,11 +503,10 @@ pub fn execute_update_start_time( } // If the new start_time is after end_time return error - if start_time > config.extension.end_time { - return Err(ContractError::InvalidStartTime( - config.extension.end_time, - start_time, - )); + if let Some(end_time) = config.extension.end_time { + if start_time > end_time { + return Err(ContractError::InvalidStartTime(end_time, start_time)); + } } config.extension.start_time = start_time; @@ -499,8 +531,13 @@ pub fn execute_update_end_time( )); } // If current time is after the stored end time return error - if env.block.time >= config.extension.end_time { - return Err(ContractError::AlreadyStarted {}); + if let Some(end_time_u) = config.extension.end_time { + if env.block.time >= end_time_u { + return Err(ContractError::AfterMintEndTime {}); + } + } else { + // Cant define a end time if it was not initially defined to have one + return Err(ContractError::NoEndTimeInitiallyDefined {}); } // If current time already passed the new end_time return error @@ -516,7 +553,7 @@ pub fn execute_update_end_time( )); } - config.extension.end_time = end_time; + config.extension.end_time = Some(end_time); CONFIG.save(deps.storage, &config)?; Ok(Response::new() .add_attribute("action", "update_end_time") @@ -636,6 +673,47 @@ pub fn mint_price(deps: Deps, is_admin: bool) -> Result { } } +pub fn execute_burn_remaining( + deps: DepsMut, + env: Env, + info: MessageInfo, +) -> Result { + nonpayable(&info)?; + let config = CONFIG.load(deps.storage)?; + // Check only admin + if info.sender != config.extension.admin { + return Err(ContractError::Unauthorized( + "Sender is not an admin".to_owned(), + )); + } + + // check mint if still time to mint + if let Some(end_time) = config.extension.end_time { + if env.block.time <= end_time { + return Err(ContractError::MintingHasNotYetEnded {}); + } + } + + // check mint not sold out + let mintable_num_tokens = MINTABLE_NUM_TOKENS.may_load(deps.storage)?; + if let Some(mintable_nb_tokens) = mintable_num_tokens { + if mintable_nb_tokens == 0 { + return Err(ContractError::SoldOut {}); + } + } + + // Decrement mintable num tokens + if mintable_num_tokens.is_some() { + MINTABLE_NUM_TOKENS.save(deps.storage, &0)?; + } + + let event = Event::new("burn-remaining") + .add_attribute("sender", info.sender) + .add_attribute("tokens_burned", mintable_num_tokens.unwrap().to_string()) + .add_attribute("minter", env.contract.address.to_string()); + Ok(Response::new().add_event(event)) +} + fn mint_count_per_addr(deps: Deps, info: &MessageInfo) -> Result { let mint_count = (MINTER_ADDRS.key(&info.sender).may_load(deps.storage)?).unwrap_or(0); Ok(mint_count) @@ -678,6 +756,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { QueryMsg::MintPrice {} => to_binary(&query_mint_price(deps)?), QueryMsg::MintCount { address } => to_binary(&query_mint_count_per_address(deps, address)?), QueryMsg::TotalMintCount {} => to_binary(&query_mint_count(deps)?), + QueryMsg::MintableNumTokens {} => to_binary(&query_mintable_num_tokens(deps)?), } } @@ -690,6 +769,7 @@ fn query_config(deps: Deps) -> StdResult { nft_data: config.extension.nft_data, payment_address: config.extension.payment_address, per_address_limit: config.extension.per_address_limit, + num_tokens: config.extension.num_tokens, end_time: config.extension.end_time, sg721_address: sg721_address.to_string(), sg721_code_id: config.collection_code_id, @@ -719,6 +799,11 @@ fn query_mint_count(deps: Deps) -> StdResult { Ok(TotalMintCountResponse { count: mint_count }) } +fn query_mintable_num_tokens(deps: Deps) -> StdResult { + let count = MINTABLE_NUM_TOKENS.may_load(deps.storage)?; + Ok(MintableNumTokensResponse { count }) +} + fn query_start_time(deps: Deps) -> StdResult { let config = CONFIG.load(deps.storage)?; Ok(StartTimeResponse { @@ -728,9 +813,15 @@ fn query_start_time(deps: Deps) -> StdResult { fn query_end_time(deps: Deps) -> StdResult { let config = CONFIG.load(deps.storage)?; - Ok(EndTimeResponse { - end_time: config.extension.end_time.to_string(), - }) + let end_time_response = config + .extension + .end_time + .map(|end_time| EndTimeResponse { + end_time: Some(end_time.to_string()), + }) + .unwrap_or(EndTimeResponse { end_time: None }); + + Ok(end_time_response) } fn query_mint_price(deps: Deps) -> StdResult { diff --git a/contracts/minters/open-edition-minter/src/error.rs b/contracts/minters/open-edition-minter/src/error.rs index b8b7233e7..c67fa83a0 100644 --- a/contracts/minters/open-edition-minter/src/error.rs +++ b/contracts/minters/open-edition-minter/src/error.rs @@ -46,6 +46,9 @@ pub enum ContractError { #[error("Sold out")] SoldOut {}, + #[error("Not Sold out")] + NotSoldOut {}, + #[error("MintingHasNotYetEnded")] MintingHasNotYetEnded {}, @@ -97,6 +100,9 @@ pub enum ContractError { #[error("Minting has not started yet")] BeforeMintStartTime {}, + #[error("No End Time Initially Defined")] + NoEndTimeInitiallyDefined {}, + #[error("Minting has ended")] AfterMintEndTime {}, diff --git a/contracts/minters/open-edition-minter/src/msg.rs b/contracts/minters/open-edition-minter/src/msg.rs index 5adc46f0e..c623c05cb 100644 --- a/contracts/minters/open-edition-minter/src/msg.rs +++ b/contracts/minters/open-edition-minter/src/msg.rs @@ -27,6 +27,7 @@ pub enum ExecuteMsg { MintTo { recipient: String, }, + BurnRemaining {}, } #[cw_serde] @@ -38,6 +39,7 @@ pub enum QueryMsg { MintCount { address: String }, TotalMintCount {}, Status {}, + MintableNumTokens {}, } #[cw_serde] @@ -46,7 +48,8 @@ pub struct ConfigResponse { pub nft_data: NftData, pub payment_address: Option, pub per_address_limit: u32, - pub end_time: Timestamp, + pub num_tokens: Option, + pub end_time: Option, pub sg721_address: String, pub sg721_code_id: u64, pub start_time: Timestamp, @@ -56,7 +59,7 @@ pub struct ConfigResponse { #[cw_serde] pub struct MintableNumTokensResponse { - pub count: u32, + pub count: Option, } #[cw_serde] @@ -66,7 +69,7 @@ pub struct StartTimeResponse { #[cw_serde] pub struct EndTimeResponse { - pub end_time: String, + pub end_time: Option, } #[cw_serde] diff --git a/contracts/minters/open-edition-minter/src/state.rs b/contracts/minters/open-edition-minter/src/state.rs index 9e2b5f462..357cca9d6 100644 --- a/contracts/minters/open-edition-minter/src/state.rs +++ b/contracts/minters/open-edition-minter/src/state.rs @@ -11,8 +11,9 @@ pub struct ConfigExtension { pub payment_address: Option, pub nft_data: NftData, pub start_time: Timestamp, - pub end_time: Timestamp, + pub end_time: Option, pub per_address_limit: u32, + pub num_tokens: Option, } pub type Config = MinterConfig; @@ -23,6 +24,8 @@ pub const MINTER_ADDRS: Map<&Addr, u32> = Map::new("ma"); /// This keeps track of the mint count pub const TOTAL_MINT_COUNT: Item = Item::new("total_mint_count"); +pub const MINTABLE_NUM_TOKENS: Item = Item::new("mintable_num_tokens"); + /// Holds the status of the minter. Can be changed with on-chain governance proposals. pub const STATUS: Item = Item::new("status"); diff --git a/e2e/src/helpers/open_edition_minter_helpers.rs b/e2e/src/helpers/open_edition_minter_helpers.rs index 566ea7d66..51ae64282 100644 --- a/e2e/src/helpers/open_edition_minter_helpers.rs +++ b/e2e/src/helpers/open_edition_minter_helpers.rs @@ -55,6 +55,7 @@ pub fn instantiate_factory( mint_fee_bps: 1000, // 10% max_trading_offset_secs: (60 * 60) * 24, extension: ParamsExtension { + max_token_limit: 1_000u32, max_per_address_limit: 50, airdrop_mint_fee_bps: 0, airdrop_mint_price: Coin { @@ -79,7 +80,8 @@ pub fn create_minter_msg( creator_addr: String, limit: u32, start_time: Timestamp, - end_time: Timestamp, + end_time: Option, + num_tokens: Option, start_trading_time: Option, nft_data: NftData, ) -> CreateMinterMsg { @@ -96,6 +98,7 @@ pub fn create_minter_msg( }, per_address_limit: limit, end_time, + num_tokens, }, collection_params: CollectionParams { code_id: code_id.unwrap_or_else(|| chain.orc.contract_map.code_id(SG721_NAME).unwrap()), diff --git a/e2e/src/tests/open_edition_factory_and_mint_tests.rs b/e2e/src/tests/open_edition_factory_and_mint_tests.rs index 09308baa8..9a1398c3f 100644 --- a/e2e/src/tests/open_edition_factory_and_mint_tests.rs +++ b/e2e/src/tests/open_edition_factory_and_mint_tests.rs @@ -46,7 +46,7 @@ fn test_create_minter(chain: &mut Chain) { instantiate_factory(chain, user_addr.clone(), dev.account.address, &user.key).unwrap(); let start_time = latest_block_time(chain).plus_seconds(10); - let end_time = latest_block_time(chain).plus_seconds(60); + let end_time = Some(latest_block_time(chain).plus_seconds(60)); let valid_minter_msg = Sg2ExecuteMsg::CreateMinter(create_minter_msg( chain, @@ -55,6 +55,7 @@ fn test_create_minter(chain: &mut Chain) { MAX_TOKENS, start_time, end_time, + Some(1_000u32), None, NftData { nft_data_type: NftMetadataType::OffChainMetadata, @@ -102,6 +103,7 @@ fn test_create_minter(chain: &mut Chain) { MAX_TOKENS, start_time, end_time, + Some(1_000u32), None, NftData { nft_data_type: NftMetadataType::OffChainMetadata, @@ -133,6 +135,7 @@ fn test_create_minter(chain: &mut Chain) { MAX_TOKENS, start_time, end_time, + Some(1_000u32), None, NftData { nft_data_type: NftMetadataType::OffChainMetadata, @@ -174,6 +177,7 @@ fn test_create_minter(chain: &mut Chain) { MAX_TOKENS, start_time, end_time, + Some(1_000u32), None, NftData { nft_data_type: NftMetadataType::OnChainMetadata, @@ -350,7 +354,7 @@ fn test_start_trading_time(chain: &mut Chain) { .unwrap(); let start_time = latest_block_time(chain).plus_seconds(5); - let end_time = latest_block_time(chain).plus_seconds(60); + let end_time = Some(latest_block_time(chain).plus_seconds(60)); // The default offset is 1 day -> 86400 let invalid_minter_msg = Sg2ExecuteMsg::CreateMinter(create_minter_msg( @@ -360,6 +364,7 @@ fn test_start_trading_time(chain: &mut Chain) { MAX_TOKENS, start_time, end_time, + Some(1_000u32), Some(start_time.plus_seconds(100_000)), NftData { nft_data_type: NftMetadataType::OffChainMetadata, @@ -392,6 +397,7 @@ fn test_start_trading_time(chain: &mut Chain) { MAX_TOKENS, start_time, end_time, + Some(1_000u32), Some(start_time.plus_seconds(80_400)), NftData { nft_data_type: NftMetadataType::OffChainMetadata, diff --git a/e2e/src/tests/open_edition_minter_executes_tests.rs b/e2e/src/tests/open_edition_minter_executes_tests.rs index c1d148848..9e077e833 100644 --- a/e2e/src/tests/open_edition_minter_executes_tests.rs +++ b/e2e/src/tests/open_edition_minter_executes_tests.rs @@ -37,7 +37,7 @@ fn test_open_edition_exec_functions(chain: &mut Chain) { .unwrap(); let start_time = latest_block_time(chain).plus_seconds(10); - let end_time = latest_block_time(chain).plus_seconds(60); + let end_time = Some(latest_block_time(chain).plus_seconds(60)); let valid_minter_msg = Sg2ExecuteMsg::CreateMinter(create_minter_msg( chain, @@ -47,6 +47,7 @@ fn test_open_edition_exec_functions(chain: &mut Chain) { start_time, end_time, None, + None, NftData { nft_data_type: NftMetadataType::OffChainMetadata, extension: None, diff --git a/test-suite/src/common_setup/msg.rs b/test-suite/src/common_setup/msg.rs index 00f58c4c0..d27e61661 100644 --- a/test-suite/src/common_setup/msg.rs +++ b/test-suite/src/common_setup/msg.rs @@ -70,6 +70,7 @@ pub struct OpenEditionMinterSetupParams<'a> { pub nft_data: NftData, pub per_address_limit: u32, pub end_time: Option, + pub num_tokens: Option, pub minter_code_id: u64, pub factory_code_id: u64, pub sg721_code_id: u64, @@ -81,6 +82,7 @@ pub struct OpenEditionMinterInstantiateParams { pub start_time: Option, pub end_time: Option, pub per_address_limit: Option, + pub num_tokens: Option, pub nft_data: Option, pub init_msg: Option, pub params_extension: Option, diff --git a/test-suite/src/common_setup/setup_minter/open_edition_minter/minter_params.rs b/test-suite/src/common_setup/setup_minter/open_edition_minter/minter_params.rs index 187b5efda..5bf69082f 100644 --- a/test-suite/src/common_setup/setup_minter/open_edition_minter/minter_params.rs +++ b/test-suite/src/common_setup/setup_minter/open_edition_minter/minter_params.rs @@ -17,16 +17,17 @@ pub fn minter_params_open_edition( init_msg: OpenEditionMinterInitMsgExtension, start_time: Option, end_time: Option, + num_tokens: Option, nft_data: Option, custom_params: Option, ) -> OpenEditionMinterInstantiateParams { let start_time = start_time.unwrap_or(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 100)); - let end_time = end_time.unwrap_or(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)); OpenEditionMinterInstantiateParams { start_time: Some(start_time), - end_time: Some(end_time), + end_time, per_address_limit: Some(init_msg.per_address_limit), + num_tokens, nft_data: Some( nft_data.unwrap_or(NftData { nft_data_type: NftMetadataType::OffChainMetadata, @@ -58,18 +59,19 @@ pub fn init_msg( per_address_limit_minter: Option, start_time: Option, end_time: Option, + num_tokens: Option, mint_price: Option, ) -> OpenEditionMinterInitMsgExtension { let start_time = start_time.unwrap_or(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 100)); - let end_time = end_time.unwrap_or(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)); let mint_price = mint_price.unwrap_or(Coin { denom: NATIVE_DENOM.to_string(), amount: Uint128::new(MIN_MINT_PRICE_OPEN_EDITION), }); mock_init_minter_extension( Some(start_time), - Some(end_time), + end_time, per_address_limit_minter, + num_tokens, Some(mint_price), nft_data, None, diff --git a/test-suite/src/common_setup/setup_minter/open_edition_minter/mock_params.rs b/test-suite/src/common_setup/setup_minter/open_edition_minter/mock_params.rs index b473aca6b..86e25566b 100644 --- a/test-suite/src/common_setup/setup_minter/open_edition_minter/mock_params.rs +++ b/test-suite/src/common_setup/setup_minter/open_edition_minter/mock_params.rs @@ -9,13 +9,14 @@ use sg_std::{GENESIS_MINT_START_TIME, NATIVE_DENOM}; use crate::common_setup::msg::OpenEditionMinterCustomParams; use crate::common_setup::setup_minter::common::constants::{ - CREATION_FEE, DEV_ADDRESS, MINT_FEE_FAIR_BURN, MIN_MINT_PRICE_OPEN_EDITION, + CREATION_FEE, DEV_ADDRESS, MAX_TOKEN_LIMIT, MINT_FEE_FAIR_BURN, MIN_MINT_PRICE_OPEN_EDITION, }; pub fn mock_init_minter_extension( start_time: Option, end_time: Option, per_address_limit_minter: Option, + num_tokens: Option, mint_price: Option, nft_data: NftData, payment_address: Option, @@ -25,8 +26,9 @@ pub fn mock_init_minter_extension( start_time: start_time.unwrap_or(Timestamp::from_nanos(GENESIS_MINT_START_TIME)), mint_price: mint_price.unwrap_or_else(|| coin(MIN_MINT_PRICE_OPEN_EDITION, NATIVE_DENOM)), per_address_limit: per_address_limit_minter.unwrap_or(1u32), - end_time: end_time.unwrap_or(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), + end_time, payment_address, + num_tokens, } } @@ -36,6 +38,7 @@ pub fn mock_create_minter( end_time: Option, mint_price: Option, per_address_limit_minter: Option, + num_tokens: Option, default_nft_data: NftData, collection_params: CollectionParams, payment_address: Option, @@ -45,6 +48,7 @@ pub fn mock_create_minter( start_time, end_time, per_address_limit_minter, + num_tokens, mint_price, default_nft_data, payment_address, @@ -73,6 +77,7 @@ pub fn mock_params_proper() -> OpenEditionMinterParams { mint_fee_bps: MINT_FEE_FAIR_BURN, max_trading_offset_secs: 60 * 60 * 24 * 7, extension: ParamsExtension { + max_token_limit: MAX_TOKEN_LIMIT, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -99,6 +104,7 @@ pub fn mock_params_custom(custom_params: OpenEditionMinterCustomParams) -> OpenE mint_fee_bps, max_trading_offset_secs: 60 * 60 * 24 * 7, extension: ParamsExtension { + max_token_limit: MAX_TOKEN_LIMIT, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -123,6 +129,7 @@ pub fn mock_params_custom_min_mint_price( mint_fee_bps: MINT_FEE_FAIR_BURN, max_trading_offset_secs: 60 * 60 * 24 * 7, extension: ParamsExtension { + max_token_limit: MAX_TOKEN_LIMIT, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price, diff --git a/test-suite/src/common_setup/setup_minter/open_edition_minter/setup.rs b/test-suite/src/common_setup/setup_minter/open_edition_minter/setup.rs index d40ea30a6..e14aab4f4 100644 --- a/test-suite/src/common_setup/setup_minter/open_edition_minter/setup.rs +++ b/test-suite/src/common_setup/setup_minter/open_edition_minter/setup.rs @@ -24,10 +24,12 @@ use crate::common_setup::setup_minter::open_edition_minter::mock_params::{ use crate::common_setup::setup_minter::common::constants::CREATION_FEE; +#[allow(clippy::too_many_arguments)] pub fn build_init_msg( init_msg: Option, start_time: Option, end_time: Option, + num_tokens: Option, per_address_limit_minter: Option, nft_data: NftData, mint_price: Option, @@ -39,6 +41,7 @@ pub fn build_init_msg( start_time, end_time, per_address_limit_minter, + num_tokens, mint_price, nft_data, payment_address, @@ -86,6 +89,7 @@ pub fn setup_open_edition_minter_contract( end_time, Some(coin(min_mint_price.u128(), denom.clone())), Some(params.extension.max_per_address_limit), + None, nft_data.clone(), collection_params, None, @@ -94,6 +98,7 @@ pub fn setup_open_edition_minter_contract( init_msg, start_time, end_time, + Some(params.extension.max_token_limit), Some(params.extension.max_per_address_limit), nft_data, Some(coin(min_mint_price.u128(), denom)), @@ -158,6 +163,7 @@ pub fn sudo_update_params( min_mint_price: None, dev_fee_address: None, max_per_address_limit: None, + max_token_limit: None, airdrop_mint_price: None, airdrop_mint_fee_bps: None, }, @@ -202,6 +208,7 @@ pub fn configure_open_edition_minter( .unwrap(), init_msg: minter_instantiate_params_vec[index].init_msg.clone(), end_time: minter_instantiate_params_vec[index].end_time.to_owned(), + num_tokens: minter_instantiate_params_vec[index].num_tokens.to_owned(), custom_params: minter_instantiate_params_vec[index].custom_params.clone(), }; let minter_collection_res = setup_open_edition_minter_contract(setup_params); diff --git a/test-suite/src/common_setup/templates.rs b/test-suite/src/common_setup/templates.rs index f5c23cf72..13016a33c 100644 --- a/test-suite/src/common_setup/templates.rs +++ b/test-suite/src/common_setup/templates.rs @@ -331,7 +331,7 @@ pub fn open_edition_minter_custom_template( let code_ids = open_edition_minter_code_ids(&mut app); let collection_params = mock_collection_params_1(None); let minter_params = - minter_params_open_edition(params_extension, init_msg, None, None, None, None); + minter_params_open_edition(params_extension, init_msg, None, None, None, None, None); let minter_collection_response = configure_open_edition_minter( &mut app, @@ -361,8 +361,15 @@ pub fn open_edition_minter_nft_data( let (creator, buyer) = setup_accounts(&mut app); let code_ids = open_edition_minter_code_ids(&mut app); let collection_params = mock_collection_params_1(None); - let minter_params = - minter_params_open_edition(params_extension, init_msg, None, None, Some(nft_data), None); + let minter_params = minter_params_open_edition( + params_extension, + init_msg, + None, + None, + None, + Some(nft_data), + None, + ); let minter_collection_response = configure_open_edition_minter( &mut app, @@ -388,8 +395,15 @@ pub fn open_edition_minter_start_and_end_time( let (creator, buyer) = setup_accounts(&mut app); let code_ids = open_edition_minter_code_ids(&mut app); let collection_params = mock_collection_params_1(None); - let minter_params = - minter_params_open_edition(params_extension, init_msg, start_time, end_time, None, None); + let minter_params = minter_params_open_edition( + params_extension, + init_msg, + start_time, + end_time, + None, + None, + None, + ); let minter_collection_response = configure_open_edition_minter( &mut app, @@ -415,7 +429,7 @@ pub fn open_edition_minter_custom_code_ids( let (creator, buyer) = setup_accounts(&mut app); let collection_params = mock_collection_params_1(None); let minter_params = - minter_params_open_edition(params_extension, init_msg, None, None, None, None); + minter_params_open_edition(params_extension, init_msg, None, None, None, None, None); let minter_collection_response = configure_open_edition_minter( &mut app, @@ -504,9 +518,10 @@ pub fn open_edition_minter_with_two_sg721_collections_burn_mint( None, None, None, + None, ); let minter_params_2 = - minter_params_open_edition(params_extension, init_msg, None, None, None, None); + minter_params_open_edition(params_extension, init_msg, None, None, None, None, None); let code_ids = open_edition_minter_code_ids(&mut router); let minter_collection_response = configure_open_edition_minter( &mut router, @@ -537,6 +552,7 @@ pub fn open_edition_minter_ibc_template( None, None, None, + None, Some(custom_minter_params), ); diff --git a/test-suite/src/open_edition_factory/tests/integration_tests.rs b/test-suite/src/open_edition_factory/tests/integration_tests.rs index c13bee504..f931e5ef3 100644 --- a/test-suite/src/open_edition_factory/tests/integration_tests.rs +++ b/test-suite/src/open_edition_factory/tests/integration_tests.rs @@ -2,11 +2,11 @@ mod tests { mod init { + use crate::common_setup::setup_minter::common::constants::MAX_TOKEN_LIMIT; use crate::open_edition_factory::tests::common::proper_instantiate; use open_edition_factory::msg::{ OpenEditionUpdateParamsExtension, OpenEditionUpdateParamsMsg, ParamsResponse, SudoMsg, }; - // Assumption: CreateMinter method is validated at the minter level #[test] @@ -26,6 +26,7 @@ mod tests { assert_eq!(res.params.allowed_sg721_code_ids, vec![1, 3, 5, 6]); assert!(!res.params.frozen); assert_eq!(res.params.mint_fee_bps, 1000); + assert_eq!(res.params.extension.max_token_limit, MAX_TOKEN_LIMIT); let update_msg = OpenEditionUpdateParamsMsg { add_sg721_code_ids: Some(vec![12, 24]), @@ -37,6 +38,7 @@ mod tests { mint_fee_bps: Some(2000), max_trading_offset_secs: None, extension: OpenEditionUpdateParamsExtension { + max_token_limit: Some(9_999u32), max_per_address_limit: None, min_mint_price: None, airdrop_mint_fee_bps: None, @@ -53,6 +55,7 @@ mod tests { assert_eq!(res.params.allowed_sg721_code_ids, vec![3, 5, 6, 12, 24]); assert!(res.params.frozen); assert_eq!(res.params.mint_fee_bps, 2000); + assert_eq!(res.params.extension.max_token_limit, 9_999u32); } } } diff --git a/test-suite/src/open_edition_factory/tests/queries.rs b/test-suite/src/open_edition_factory/tests/queries.rs index 83388c750..32e927f0d 100644 --- a/test-suite/src/open_edition_factory/tests/queries.rs +++ b/test-suite/src/open_edition_factory/tests/queries.rs @@ -80,6 +80,10 @@ mod tests { res.params.extension.dev_fee_address, params.extension.dev_fee_address ); + assert_eq!( + res.params.extension.max_token_limit, + params.extension.max_token_limit + ); } #[test] diff --git a/test-suite/src/open_edition_factory/tests/sudo_tests.rs b/test-suite/src/open_edition_factory/tests/sudo_tests.rs index 5d25d2026..1692bc4ff 100644 --- a/test-suite/src/open_edition_factory/tests/sudo_tests.rs +++ b/test-suite/src/open_edition_factory/tests/sudo_tests.rs @@ -5,15 +5,16 @@ use crate::common_setup::setup_minter::open_edition_minter::minter_params::{ use crate::common_setup::setup_minter::open_edition_minter::setup::sudo_update_params; use crate::common_setup::templates::open_edition_minter_custom_template; use base_factory::msg::ParamsResponse; -use cosmwasm_std::{coin, Coin, Uint128}; +use cosmwasm_std::{coin, Coin, Timestamp, Uint128}; use open_edition_factory::msg::OpenEditionUpdateParamsExtension; use open_edition_factory::state::ParamsExtension; use sg2::query::Sg2QueryMsg::Params; -use sg_std::NATIVE_DENOM; +use sg_std::{GENESIS_MINT_START_TIME, NATIVE_DENOM}; #[test] fn happy_path_with_params_update() { let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -27,6 +28,7 @@ fn happy_path_with_params_update() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -38,6 +40,7 @@ fn happy_path_with_params_update() { #[test] fn sudo_params_update_creation_fee() { let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -51,6 +54,7 @@ fn sudo_params_update_creation_fee() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -69,6 +73,7 @@ fn sudo_params_update_creation_fee() { mint_fee_bps: None, max_trading_offset_secs: Some(100), extension: OpenEditionUpdateParamsExtension { + max_token_limit: None, min_mint_price: Some(coin(10, NATIVE_DENOM)), max_per_address_limit: None, airdrop_mint_price: None, diff --git a/test-suite/src/open_edition_minter/tests.rs b/test-suite/src/open_edition_minter/tests.rs index 009f57211..03bf7c20b 100644 --- a/test-suite/src/open_edition_minter/tests.rs +++ b/test-suite/src/open_edition_minter/tests.rs @@ -4,5 +4,6 @@ mod complete_mint_all_outcomes_validation; mod factory_create_minter; mod frozen_factory; mod ibc_asset_mint; +mod max_tokens_limit; mod update_mint_price; mod update_start_and_end_time; diff --git a/test-suite/src/open_edition_minter/tests/address_limit.rs b/test-suite/src/open_edition_minter/tests/address_limit.rs index 00003db17..af003588c 100644 --- a/test-suite/src/open_edition_minter/tests/address_limit.rs +++ b/test-suite/src/open_edition_minter/tests/address_limit.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{coins, Coin, Uint128}; +use cosmwasm_std::{coins, Coin, Timestamp, Uint128}; use cw_multi_test::Executor; use open_edition_factory::state::ParamsExtension; use sg_std::{GENESIS_MINT_START_TIME, NATIVE_DENOM}; @@ -18,6 +18,7 @@ const MINT_PRICE: u128 = 100_000_000; #[test] fn check_per_address_limit() { let params_extension = ParamsExtension { + max_token_limit: 10_000, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -31,6 +32,7 @@ fn check_per_address_limit() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -41,7 +43,7 @@ fn check_per_address_limit() { // Set to a valid mint time setup_block_time(&mut router, GENESIS_MINT_START_TIME + 101, None); - // Check the Config + // Check the Config for per address limit let query_config_msg = QueryMsg::Config {}; let res: ConfigResponse = router .wrap() diff --git a/test-suite/src/open_edition_minter/tests/allowed_code_ids.rs b/test-suite/src/open_edition_minter/tests/allowed_code_ids.rs index fc4acbc41..b9f285f2c 100644 --- a/test-suite/src/open_edition_minter/tests/allowed_code_ids.rs +++ b/test-suite/src/open_edition_minter/tests/allowed_code_ids.rs @@ -1,6 +1,6 @@ -use cosmwasm_std::{Coin, Uint128}; +use cosmwasm_std::{Coin, Timestamp, Uint128}; use open_edition_factory::state::ParamsExtension; -use sg_std::NATIVE_DENOM; +use sg_std::{GENESIS_MINT_START_TIME, NATIVE_DENOM}; use crate::common_setup::{ contract_boxes::custom_mock_app, @@ -17,6 +17,7 @@ use crate::common_setup::{ #[test] fn invalid_code_id() { let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -30,6 +31,7 @@ fn invalid_code_id() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); diff --git a/test-suite/src/open_edition_minter/tests/complete_mint_all_outcomes_validation.rs b/test-suite/src/open_edition_minter/tests/complete_mint_all_outcomes_validation.rs index e614c49ec..90204b742 100644 --- a/test-suite/src/open_edition_minter/tests/complete_mint_all_outcomes_validation.rs +++ b/test-suite/src/open_edition_minter/tests/complete_mint_all_outcomes_validation.rs @@ -4,8 +4,10 @@ use cw_multi_test::{BankSudo, Executor, SudoMsg}; use open_edition_factory::state::ParamsExtension; use sg_std::{GENESIS_MINT_START_TIME, NATIVE_DENOM}; -use open_edition_minter::msg::{EndTimeResponse, ExecuteMsg, QueryMsg, TotalMintCountResponse}; -use open_edition_minter::msg::{MintCountResponse, MintPriceResponse, StartTimeResponse}; +use open_edition_minter::msg::{ + ConfigResponse, EndTimeResponse, ExecuteMsg, MintCountResponse, MintPriceResponse, + MintableNumTokensResponse, QueryMsg, StartTimeResponse, TotalMintCountResponse, +}; use sg4::StatusResponse; use crate::common_setup::setup_accounts_and_block::{coins_for_msg, setup_block_time}; @@ -17,9 +19,9 @@ use crate::common_setup::templates::open_edition_minter_custom_template; const MINT_PRICE: u128 = 100_000_000; -#[test] -fn check_mint_revenues_distribution() { +fn check_mint_revenues_distribution(num_tokens: Option, end_minter_time: Option) { let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -33,7 +35,8 @@ fn check_mint_revenues_distribution() { default_nft_data(), per_address_limit_minter, None, - None, + end_minter_time, + num_tokens, None, ); let vt = open_edition_minter_custom_template(params_extension, init_msg).unwrap(); @@ -73,10 +76,40 @@ fn check_mint_revenues_distribution() { .wrap() .query_wasm_smart(minter_addr.clone(), &query_end_time_msg) .unwrap(); - assert_eq!( - res.end_time, - Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000).to_string() - ); + if end_minter_time.is_some() { + assert_eq!( + res.end_time, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000).to_string()) + ); + } else { + assert_eq!(res.end_time, None); + } + + // Query the Max Tokens or End Time depending on which test is executed + let query_config_msg = QueryMsg::MintableNumTokens {}; + let res: MintableNumTokensResponse = router + .wrap() + .query_wasm_smart(minter_addr.clone(), &query_config_msg) + .unwrap(); + if end_minter_time.is_some() { + assert_eq!(res.count, None); + } else { + assert_eq!(res.count, Some(5)); + } + + // Query the Config info + let query_config_msg = QueryMsg::Config {}; + let res: ConfigResponse = router + .wrap() + .query_wasm_smart(minter_addr.clone(), &query_config_msg) + .unwrap(); + if end_minter_time.is_some() { + assert_eq!(res.num_tokens, None); + assert_eq!(res.end_time, end_minter_time); + } else { + assert_eq!(res.num_tokens, Some(5)); + assert_eq!(res.end_time, None); + } // Query Total Minted Tokens -> Should be 0 at the start let query_total_minted_msg: QueryMsg = QueryMsg::TotalMintCount {}; @@ -284,10 +317,14 @@ fn check_mint_revenues_distribution() { &mint_msg, &coins(MINT_PRICE, NATIVE_DENOM), ); - assert_eq!( - res.err().unwrap().source().unwrap().to_string(), - "Minting has ended" - ); + if end_minter_time.is_some() { + assert_eq!( + res.err().unwrap().source().unwrap().to_string(), + "Minting has ended" + ); + } else { + assert!(res.is_ok()); + } // Try to execute admin only entry point from buyer let exec_msg = ExecuteMsg::MintTo { @@ -320,7 +357,7 @@ fn check_mint_revenues_distribution() { .unwrap(); assert_eq!(minter_balance.len(), 0); - // Creator can't use MintTo if sold out + // Creator can't use MintTo if the end time is < block time let res = router.execute_contract( creator.clone(), minter_addr.clone(), @@ -330,7 +367,41 @@ fn check_mint_revenues_distribution() { denom: NATIVE_DENOM.to_string(), }), ); - assert!(res.is_err()); + if end_minter_time.is_some() { + assert!(res.is_err()); + } else { + assert!(res.is_ok()); + } + + // Check if the count is accurate depending on the config of the test + // if no end time -> should be at 5 otherwise 3 as it would not be possible do use the mint to + let query_config_msg = QueryMsg::TotalMintCount {}; + let res: TotalMintCountResponse = router + .wrap() + .query_wasm_smart(minter_addr.clone(), &query_config_msg) + .unwrap(); + if end_minter_time.is_some() { + assert_eq!(res.count, 3); + } else { + assert_eq!(res.count, 5); + } + + // It should not be possible to mint anymore in both cases + let mint_msg = ExecuteMsg::Mint {}; + let res = router.execute_contract( + creator.clone(), + minter_addr.clone(), + &mint_msg, + &coins(MINT_PRICE, NATIVE_DENOM), + ); + if end_minter_time.is_some() { + assert_eq!( + res.err().unwrap().source().unwrap().to_string(), + "Minting has ended" + ); + } else { + assert_eq!(res.err().unwrap().source().unwrap().to_string(), "Sold out"); + } // Can purge after sold out let purge_msg = ExecuteMsg::Purge {}; @@ -349,3 +420,16 @@ fn check_mint_revenues_distribution() { .unwrap(); assert_eq!(res.count, 0); } + +#[test] +fn check_mint_revenues_distribution_without_end_time() { + check_mint_revenues_distribution( + None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), + ) +} + +#[test] +fn check_mint_revenues_distribution_with_end_time() { + check_mint_revenues_distribution(Some(5u32), None) +} diff --git a/test-suite/src/open_edition_minter/tests/factory_create_minter.rs b/test-suite/src/open_edition_minter/tests/factory_create_minter.rs index 595d547e8..7d88ebb2c 100644 --- a/test-suite/src/open_edition_minter/tests/factory_create_minter.rs +++ b/test-suite/src/open_edition_minter/tests/factory_create_minter.rs @@ -3,7 +3,7 @@ use open_edition_factory::state::ParamsExtension; use sg_std::{GENESIS_MINT_START_TIME, NATIVE_DENOM}; use crate::common_setup::setup_minter::common::constants::{ - DEV_ADDRESS, MIN_MINT_PRICE_OPEN_EDITION, + DEV_ADDRESS, MAX_TOKEN_LIMIT, MIN_MINT_PRICE_OPEN_EDITION, }; use crate::common_setup::setup_minter::open_edition_minter::minter_params::{ default_nft_data, init_msg, @@ -23,6 +23,7 @@ fn check_valid_create_minter() { // Set a per address lower or equal than the factory -> ok let max_per_address_limit = 10; let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -31,11 +32,12 @@ fn check_valid_create_minter() { }, dev_fee_address: DEV_ADDRESS.to_string(), }; - let per_address_limit_minter = Some(2); + let per_address_limit_minter = Some(5); let init_msg = init_msg( default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -43,6 +45,36 @@ fn check_valid_create_minter() { assert!(vt.collection_response_vec[0].error.is_none()) } +#[test] +fn check_custom_denom_create_minter() { + // Set a per address lower or equal than the factory -> ok + let max_per_address_limit = 10; + let params_extension = ParamsExtension { + max_token_limit: 10, + max_per_address_limit, + airdrop_mint_fee_bps: 100, + airdrop_mint_price: Coin { + denom: "ibc/frenz".to_string(), + amount: Uint128::new(100_000_000u128), + }, + dev_fee_address: DEV_ADDRESS.to_string(), + }; + let per_address_limit_minter = Some(5); + let init_msg = init_msg( + default_nft_data(), + per_address_limit_minter, + None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), + None, + Some(Coin { + denom: "ibc/frenz".to_string(), + amount: Uint128::new(100_000_000u128), + }), + ); + let vt = open_edition_minter_custom_template(params_extension, init_msg); + assert!(vt.is_ok()) +} + #[test] fn check_invalid_create_minter_address_limit() { // If the absolute max per address defined in the factory is 10 and the message to init the @@ -50,6 +82,7 @@ fn check_invalid_create_minter_address_limit() { let max_per_address_limit = 10; let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -63,6 +96,7 @@ fn check_invalid_create_minter_address_limit() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -82,6 +116,7 @@ fn check_invalid_create_minter_address_limit() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -100,6 +135,7 @@ fn check_invalid_create_minter_address_limit() { #[test] fn check_invalid_create_minter_start_end_time() { let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -114,6 +150,7 @@ fn check_invalid_create_minter_start_end_time() { default_nft_data(), per_address_limit_minter, start_time, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -142,6 +179,7 @@ fn check_invalid_create_minter_start_end_time() { start_time, end_time, None, + None, ); let vt = open_edition_minter_start_and_end_time(params_extension, init_msg_1, start_time, end_time) @@ -162,6 +200,7 @@ fn check_invalid_create_minter_start_end_time() { fn check_invalid_create_minter_mint_price() { // Invalid denom let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -175,6 +214,7 @@ fn check_invalid_create_minter_mint_price() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, Some(Coin { denom: "uinvalid".to_string(), @@ -196,6 +236,7 @@ fn check_invalid_create_minter_mint_price() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, Some(Coin { denom: NATIVE_DENOM.to_string(), @@ -217,6 +258,7 @@ fn check_invalid_create_minter_mint_price() { #[test] fn check_invalid_create_minter_nft_data() { let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -236,6 +278,7 @@ fn check_invalid_create_minter_nft_data() { nft_data_1.clone(), per_address_limit_minter, start_time, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -280,6 +323,7 @@ fn check_invalid_create_minter_nft_data() { nft_data_2.clone(), per_address_limit_minter, start_time, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -311,6 +355,7 @@ fn check_invalid_create_minter_nft_data() { nft_data_3.clone(), per_address_limit_minter, start_time, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -327,3 +372,41 @@ fn check_invalid_create_minter_nft_data() { "InvalidNftDataProvided" ); } + +#[test] +fn check_invalid_create_minter_max_tokens() { + // Invalid max tokens + let params_extension = ParamsExtension { + max_token_limit: MAX_TOKEN_LIMIT, + max_per_address_limit: 10, + airdrop_mint_fee_bps: 100, + airdrop_mint_price: Coin { + denom: NATIVE_DENOM.to_string(), + amount: Uint128::new(100_000_000u128), + }, + dev_fee_address: DEV_ADDRESS.to_string(), + }; + let per_address_limit_minter = Some(2); + let init_msg_1 = init_msg( + default_nft_data(), + per_address_limit_minter, + None, + None, + Some(MAX_TOKEN_LIMIT + 1), + None, + ); + let vt = open_edition_minter_custom_template(params_extension.clone(), init_msg_1).unwrap(); + assert!(vt.collection_response_vec[0].error.is_some()); + + // Number of Tokens and End Time are both None + let init_msg_2 = init_msg( + default_nft_data(), + per_address_limit_minter, + None, + None, + None, + None, + ); + let vt = open_edition_minter_custom_template(params_extension, init_msg_2).unwrap(); + assert!(vt.collection_response_vec[0].error.is_some()); +} diff --git a/test-suite/src/open_edition_minter/tests/frozen_factory.rs b/test-suite/src/open_edition_minter/tests/frozen_factory.rs index b10489a6c..8081a900a 100644 --- a/test-suite/src/open_edition_minter/tests/frozen_factory.rs +++ b/test-suite/src/open_edition_minter/tests/frozen_factory.rs @@ -19,6 +19,7 @@ use crate::common_setup::templates::open_edition_minter_custom_template; #[test] fn frozen_factory_cannot_create_new_minters() { let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -27,7 +28,14 @@ fn frozen_factory_cannot_create_new_minters() { }, dev_fee_address: DEV_ADDRESS.to_string(), }; - let init_msg = init_msg(default_nft_data(), None, None, None, None); + let init_msg = init_msg( + default_nft_data(), + None, + None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), + None, + None, + ); let vt = open_edition_minter_custom_template(params_extension, init_msg).unwrap(); let (mut router, creator, _buyer) = (vt.router, vt.accts.creator, vt.accts.buyer); @@ -44,6 +52,7 @@ fn frozen_factory_cannot_create_new_minters() { mint_fee_bps: None, max_trading_offset_secs: None, extension: OpenEditionUpdateParamsExtension { + max_token_limit: None, max_per_address_limit: None, min_mint_price: None, airdrop_mint_fee_bps: None, @@ -76,6 +85,7 @@ fn frozen_factory_cannot_create_new_minters() { Some(end_time), mint_price, per_address_limit_minter, + None, default_nft_data, collection_params, None, diff --git a/test-suite/src/open_edition_minter/tests/ibc_asset_mint.rs b/test-suite/src/open_edition_minter/tests/ibc_asset_mint.rs index 1d4e05f3f..422289d42 100644 --- a/test-suite/src/open_edition_minter/tests/ibc_asset_mint.rs +++ b/test-suite/src/open_edition_minter/tests/ibc_asset_mint.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{coin, Addr, Coin, Decimal, Uint128}; +use cosmwasm_std::{coin, Addr, Coin, Decimal, Timestamp, Uint128}; use cw_multi_test::{BankSudo, Executor, SudoMsg}; use open_edition_factory::state::{OpenEditionMinterParams, ParamsExtension}; use open_edition_minter::msg::ExecuteMsg; @@ -21,6 +21,7 @@ fn check_custom_create_minter_denom() { let denom = "ibc/frenz"; let mint_price = coin(MIN_MINT_PRICE_OPEN_EDITION, denom.to_string()); let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -34,6 +35,7 @@ fn check_custom_create_minter_denom() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, Some(mint_price.clone()), ); @@ -46,6 +48,7 @@ fn check_custom_create_minter_denom() { mint_fee_bps: MINT_FEE_FAIR_BURN, max_trading_offset_secs: 60 * 60 * 24 * 7, extension: ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, dev_fee_address: DEV_ADDRESS.to_string(), @@ -96,6 +99,7 @@ fn one_hundred_percent_burned_ibc_minter() { let denom = "ibc/frenz"; let mint_price = coin(MIN_MINT_PRICE_OPEN_EDITION, denom.to_string()); let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -109,6 +113,7 @@ fn one_hundred_percent_burned_ibc_minter() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, Some(mint_price.clone()), ); @@ -122,6 +127,7 @@ fn one_hundred_percent_burned_ibc_minter() { mint_fee_bps: 10000, max_trading_offset_secs: 60 * 60 * 24 * 7, extension: ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, dev_fee_address: DEV_ADDRESS.to_string(), @@ -177,6 +183,7 @@ fn zero_mint_fee() { let mint_price = coin(MIN_MINT_PRICE_OPEN_EDITION, denom.to_string()); let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -190,6 +197,7 @@ fn zero_mint_fee() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, Some(mint_price.clone()), ); @@ -203,6 +211,7 @@ fn zero_mint_fee() { mint_fee_bps: 0, max_trading_offset_secs: 60 * 60 * 24 * 7, extension: ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, dev_fee_address: DEV_ADDRESS.to_string(), diff --git a/test-suite/src/open_edition_minter/tests/max_tokens_limit.rs b/test-suite/src/open_edition_minter/tests/max_tokens_limit.rs new file mode 100644 index 000000000..3b9d3da4e --- /dev/null +++ b/test-suite/src/open_edition_minter/tests/max_tokens_limit.rs @@ -0,0 +1,92 @@ +use cosmwasm_std::{coins, Coin, Timestamp, Uint128}; +use cw_multi_test::Executor; +use open_edition_factory::state::ParamsExtension; +use sg_std::{GENESIS_MINT_START_TIME, NATIVE_DENOM}; + +use open_edition_minter::msg::ConfigResponse; +use open_edition_minter::msg::{ExecuteMsg, QueryMsg}; + +use crate::common_setup::setup_accounts_and_block::setup_block_time; +use crate::common_setup::setup_minter::common::constants::{DEV_ADDRESS, MAX_TOKEN_LIMIT}; +use crate::common_setup::setup_minter::open_edition_minter::minter_params::{ + default_nft_data, init_msg, +}; +use crate::common_setup::templates::open_edition_minter_custom_template; + +const MINT_PRICE: u128 = 100_000_000; + +#[test] +fn check_max_tokens_limit_init() { + let params_extension = ParamsExtension { + max_token_limit: 10, + max_per_address_limit: 10, + airdrop_mint_fee_bps: 100, + airdrop_mint_price: Coin { + denom: NATIVE_DENOM.to_string(), + amount: Uint128::new(100_000_000u128), + }, + dev_fee_address: DEV_ADDRESS.to_string(), + }; + // if the number of tokens to be minted exceed to max, should error + let per_address_limit_minter = Some(2); + let init_msg = init_msg( + default_nft_data(), + per_address_limit_minter, + None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), + Some(MAX_TOKEN_LIMIT + 1), + None, + ); + let vt = open_edition_minter_custom_template(params_extension.clone(), init_msg).unwrap(); + assert!(vt.collection_response_vec[0].error.is_some()); + // Should work otherwise + let init_msg = crate::common_setup::setup_minter::open_edition_minter::minter_params::init_msg( + default_nft_data(), + per_address_limit_minter, + None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), + Some(2), + None, + ); + let vt = open_edition_minter_custom_template(params_extension, init_msg); + assert!(vt.is_ok()); + + let response = vt.unwrap(); + let (mut router, creator, buyer) = ( + response.router, + response.accts.creator, + response.accts.buyer, + ); + let minter_addr = response.collection_response_vec[0].minter.clone().unwrap(); + // Set to a valid mint time + setup_block_time(&mut router, GENESIS_MINT_START_TIME + 101, None); + + // Check the Config for the num_tokens value + let query_config_msg = QueryMsg::Config {}; + let res: ConfigResponse = router + .wrap() + .query_wasm_smart(minter_addr.clone(), &query_config_msg) + .unwrap(); + assert_eq!(res.num_tokens, Some(2)); + + // Only the first 2 mints + for _ in 1..=2 { + let mint_msg = ExecuteMsg::Mint {}; + let res = router.execute_contract( + buyer.clone(), + minter_addr.clone(), + &mint_msg, + &coins(MINT_PRICE, NATIVE_DENOM), + ); + assert!(res.is_ok()); + } + // 3rd mint fails from exceeding num of tokens + let mint_msg = ExecuteMsg::Mint {}; + let res = router.execute_contract( + creator, + minter_addr, + &mint_msg, + &coins(MINT_PRICE, NATIVE_DENOM), + ); + assert_eq!(res.err().unwrap().source().unwrap().to_string(), "Sold out"); +} diff --git a/test-suite/src/open_edition_minter/tests/update_mint_price.rs b/test-suite/src/open_edition_minter/tests/update_mint_price.rs index d06f0b28d..4b56912ae 100644 --- a/test-suite/src/open_edition_minter/tests/update_mint_price.rs +++ b/test-suite/src/open_edition_minter/tests/update_mint_price.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Coin, Uint128}; +use cosmwasm_std::{Coin, Timestamp, Uint128}; use cw_multi_test::Executor; use open_edition_factory::state::ParamsExtension; use sg_std::{GENESIS_MINT_START_TIME, NATIVE_DENOM}; @@ -17,6 +17,7 @@ const MINT_PRICE: u128 = 100_000_000; #[test] fn check_mint_price_updates() { let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -30,6 +31,7 @@ fn check_mint_price_updates() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); diff --git a/test-suite/src/open_edition_minter/tests/update_start_and_end_time.rs b/test-suite/src/open_edition_minter/tests/update_start_and_end_time.rs index ef2524d54..bd8c4be5e 100644 --- a/test-suite/src/open_edition_minter/tests/update_start_and_end_time.rs +++ b/test-suite/src/open_edition_minter/tests/update_start_and_end_time.rs @@ -15,6 +15,7 @@ use crate::common_setup::templates::open_edition_minter_custom_template; #[test] fn check_start_end_time_updates() { let params_extension = ParamsExtension { + max_token_limit: 10, max_per_address_limit: 10, airdrop_mint_fee_bps: 100, airdrop_mint_price: Coin { @@ -28,6 +29,7 @@ fn check_start_end_time_updates() { default_nft_data(), per_address_limit_minter, None, + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000)), None, None, ); @@ -56,7 +58,7 @@ fn check_start_end_time_updates() { .unwrap(); assert_eq!( res.end_time, - Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000).to_string() + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000).to_string()) ); // Cant change start time to before the current time @@ -141,6 +143,6 @@ fn check_start_end_time_updates() { .unwrap(); assert_eq!( res.end_time, - Timestamp::from_nanos(GENESIS_MINT_START_TIME + 20_000).to_string() + Some(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 20_000).to_string()) ); }