use crate::contract::instantiate;
use crate::msg::{
ConfigResponse, ExecuteMsg, MintCountResponse, MintPriceResponse, MintableNumTokensResponse,
QueryMsg, StartTimeResponse,
};
use crate::ContractError;
use cosmwasm_std::testing::{mock_dependencies_with_balance, mock_env, mock_info};
use cosmwasm_std::{coin, coins, Addr, Empty, Timestamp, Uint128};
use cosmwasm_std::{Api, Coin};
use cw721::{Cw721QueryMsg, OwnerOfResponse, TokensResponse};
use cw721_base::ExecuteMsg as Cw721ExecuteMsg;
use cw_multi_test::{BankSudo, Contract, ContractWrapper, Executor, SudoMsg};
use sg2::msg::Sg2ExecuteMsg;
use sg2::tests::mock_collection_params;
use sg_multi_test::StargazeApp;
use sg_std::{StargazeMsgWrapper, GENESIS_MINT_START_TIME, NATIVE_DENOM};
use sg_whitelist::msg::InstantiateMsg as WhitelistInstantiateMsg;
use sg_whitelist::msg::{AddMembersMsg, ExecuteMsg as WhitelistExecuteMsg};
use vending_factory::msg::{VendingMinterCreateMsg, VendingMinterInitMsgExtension};
use vending_factory::state::{ParamsExtension, VendingMinterParams};
const CREATION_FEE: u128 = 5_000_000_000;
const INITIAL_BALANCE: u128 = 2_000_000_000;
const MINT_PRICE: u128 = 100_000_000;
const MINT_FEE: u128 = 10_000_000;
const WHITELIST_AMOUNT: u128 = 66_000_000;
const WL_PER_ADDRESS_LIMIT: u32 = 1;
const ADMIN_MINT_PRICE: u128 = 15_000_000;
const MAX_TOKEN_LIMIT: u32 = 10000;
pub const MIN_MINT_PRICE: u128 = 50_000_000;
pub const AIRDROP_MINT_PRICE: u128 = 15_000_000;
pub const MINT_FEE_BPS: u64 = 1_000; pub const AIRDROP_MINT_FEE_BPS: u64 = 10_000; pub const SHUFFLE_FEE: u128 = 500_000_000;
pub const MAX_PER_ADDRESS_LIMIT: u32 = 50;
fn custom_mock_app() -> StargazeApp {
StargazeApp::default()
}
pub fn contract_factory() -> Box<dyn Contract<StargazeMsgWrapper>> {
let contract = ContractWrapper::new(
vending_factory::contract::execute,
vending_factory::contract::instantiate,
vending_factory::contract::query,
);
Box::new(contract)
}
pub fn contract_whitelist() -> Box<dyn Contract<StargazeMsgWrapper>> {
let contract = ContractWrapper::new(
sg_whitelist::contract::execute,
sg_whitelist::contract::instantiate,
sg_whitelist::contract::query,
);
Box::new(contract)
}
pub fn contract_minter() -> Box<dyn Contract<StargazeMsgWrapper>> {
let contract = ContractWrapper::new(
crate::contract::execute,
crate::contract::instantiate,
crate::contract::query,
)
.with_reply(crate::contract::reply);
Box::new(contract)
}
pub fn contract_sg721() -> Box<dyn Contract<StargazeMsgWrapper>> {
let contract = ContractWrapper::new(
sg721_base::entry::execute,
sg721_base::entry::instantiate,
sg721_base::entry::query,
);
Box::new(contract)
}
fn setup_whitelist_contract(router: &mut StargazeApp, creator: &Addr) -> Addr {
let whitelist_code_id = router.store_code(contract_whitelist());
let msg = WhitelistInstantiateMsg {
members: vec![],
start_time: Timestamp::from_nanos(GENESIS_MINT_START_TIME + 100),
end_time: Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10000000),
mint_price: coin(WHITELIST_AMOUNT, NATIVE_DENOM),
per_address_limit: WL_PER_ADDRESS_LIMIT,
member_limit: 1000,
};
router
.instantiate_contract(
whitelist_code_id,
creator.clone(),
&msg,
&[coin(100_000_000, NATIVE_DENOM)],
"whitelist",
None,
)
.unwrap()
}
pub fn mock_params() -> VendingMinterParams {
VendingMinterParams {
code_id: 1,
creation_fee: coin(CREATION_FEE, NATIVE_DENOM),
min_mint_price: coin(MIN_MINT_PRICE, NATIVE_DENOM),
mint_fee_bps: MINT_FEE_BPS,
extension: ParamsExtension {
max_token_limit: MAX_TOKEN_LIMIT,
max_per_address_limit: MAX_PER_ADDRESS_LIMIT,
airdrop_mint_price: coin(AIRDROP_MINT_PRICE, NATIVE_DENOM),
airdrop_mint_fee_bps: AIRDROP_MINT_FEE_BPS,
shuffle_fee: coin(SHUFFLE_FEE, NATIVE_DENOM),
},
}
}
pub fn mock_init_extension() -> VendingMinterInitMsgExtension {
VendingMinterInitMsgExtension {
base_token_uri: "ipfs://aldkfjads".to_string(),
start_time: Timestamp::from_nanos(GENESIS_MINT_START_TIME),
num_tokens: 100,
mint_price: coin(MIN_MINT_PRICE, NATIVE_DENOM),
per_address_limit: 5,
whitelist: None,
}
}
pub fn mock_create_minter() -> VendingMinterCreateMsg {
VendingMinterCreateMsg {
init_msg: mock_init_extension(),
collection_params: mock_collection_params(),
}
}
fn setup_minter_contract(
router: &mut StargazeApp,
creator: &Addr,
num_tokens: u32,
) -> (Addr, ConfigResponse) {
let minter_code_id = router.store_code(contract_minter());
let creation_fee = coins(CREATION_FEE, NATIVE_DENOM);
let factory_code_id = router.store_code(contract_factory());
let mut params = mock_params();
params.code_id = minter_code_id;
let factory_addr = router
.instantiate_contract(
factory_code_id,
creator.clone(),
&vending_factory::msg::InstantiateMsg { params },
&[],
"factory",
None,
)
.unwrap();
let sg721_code_id = router.store_code(contract_sg721());
let mut msg = mock_create_minter();
msg.init_msg.mint_price = coin(MINT_PRICE, NATIVE_DENOM);
msg.init_msg.num_tokens = num_tokens;
msg.collection_params.code_id = sg721_code_id;
msg.collection_params.info.creator = creator.to_string();
let msg = Sg2ExecuteMsg::CreateMinter(msg);
let res = router.execute_contract(creator.clone(), factory_addr, &msg, &creation_fee);
assert!(res.is_ok());
let minter_addr = Addr::unchecked("contract1");
let config: ConfigResponse = router
.wrap()
.query_wasm_smart(minter_addr.clone(), &QueryMsg::Config {})
.unwrap();
(minter_addr, config)
}
fn setup_accounts(router: &mut StargazeApp) -> (Addr, Addr) {
let buyer = Addr::unchecked("buyer");
let creator = Addr::unchecked("creator");
let creator_funds = coins(INITIAL_BALANCE + CREATION_FEE, NATIVE_DENOM);
let buyer_funds = coins(INITIAL_BALANCE, NATIVE_DENOM);
router
.sudo(SudoMsg::Bank({
BankSudo::Mint {
to_address: creator.to_string(),
amount: creator_funds.clone(),
}
}))
.map_err(|err| println!("{:?}", err))
.ok();
router
.sudo(SudoMsg::Bank({
BankSudo::Mint {
to_address: buyer.to_string(),
amount: buyer_funds.clone(),
}
}))
.map_err(|err| println!("{:?}", err))
.ok();
let creator_native_balances = router.wrap().query_all_balances(creator.clone()).unwrap();
assert_eq!(creator_native_balances, creator_funds);
let buyer_native_balances = router.wrap().query_all_balances(buyer.clone()).unwrap();
assert_eq!(buyer_native_balances, buyer_funds);
(creator, buyer)
}
fn setup_block_time(router: &mut StargazeApp, nanos: u64, height: Option<u64>) {
let mut block = router.block_info();
block.time = Timestamp::from_nanos(nanos);
if let Some(h) = height {
block.height = h;
}
router.set_block(block);
}
fn coins_for_msg(msg_coin: Coin) -> Vec<Coin> {
if msg_coin.amount > Uint128::zero() {
vec![msg_coin]
} else {
vec![]
}
}
#[test]
fn initialization() {
let mut deps = mock_dependencies_with_balance(&coins(2, "token"));
let addr = "earth1";
let res = deps.api.addr_validate(&(*addr));
assert!(res.is_ok());
let info = mock_info("creator", &coins(INITIAL_BALANCE, NATIVE_DENOM));
let mut msg = mock_create_minter();
msg.init_msg.num_tokens = 100;
msg.collection_params.code_id = 1;
msg.collection_params.info.creator = info.sender.to_string();
instantiate(deps.as_mut(), mock_env(), info, msg.clone()).unwrap_err();
let info = mock_info("creator", &coins(INITIAL_BALANCE, NATIVE_DENOM));
instantiate(deps.as_mut(), mock_env(), info, msg).unwrap_err();
let wrong_denom = "uosmo";
let info = mock_info("creator", &coins(INITIAL_BALANCE, NATIVE_DENOM));
let mut msg = mock_create_minter();
msg.init_msg.mint_price = coin(MINT_PRICE, wrong_denom);
instantiate(deps.as_mut(), mock_env(), info, msg).unwrap_err();
let info = mock_info("creator", &coins(INITIAL_BALANCE, NATIVE_DENOM));
let mut msg = mock_create_minter();
msg.init_msg.mint_price = coin(1, NATIVE_DENOM);
instantiate(deps.as_mut(), mock_env(), info, msg).unwrap_err();
let info = mock_info("creator", &coins(INITIAL_BALANCE, NATIVE_DENOM));
let mut msg = mock_create_minter();
msg.init_msg.mint_price = coin(MINT_PRICE, NATIVE_DENOM);
msg.init_msg.num_tokens = MAX_TOKEN_LIMIT + 1;
instantiate(deps.as_mut(), mock_env(), info, msg).unwrap_err();
let info = mock_info("creator", &coins(INITIAL_BALANCE, NATIVE_DENOM));
let mut msg = mock_create_minter();
msg.init_msg.num_tokens = 0;
instantiate(deps.as_mut(), mock_env(), info, msg).unwrap_err();
}
#[test]
fn happy_path() {
let mut router = custom_mock_app();
setup_block_time(&mut router, GENESIS_MINT_START_TIME - 1, None);
let (creator, buyer) = setup_accounts(&mut router);
let num_tokens = 2;
let (minter_addr, config) = setup_minter_contract(&mut router, &creator, num_tokens);
let res: StartTimeResponse = router
.wrap()
.query_wasm_smart(minter_addr.clone(), &QueryMsg::StartTime {})
.unwrap();
assert_eq!(
res.start_time,
Timestamp::from_nanos(GENESIS_MINT_START_TIME).to_string()
);
setup_block_time(&mut router, GENESIS_MINT_START_TIME + 1, None);
let mint_msg = ExecuteMsg::Mint {};
let err = router.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_msg,
&coins(MINT_PRICE + 100, NATIVE_DENOM),
);
assert!(err.is_err());
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());
let creator_balances = router.wrap().query_all_balances(creator.clone()).unwrap();
assert_eq!(
creator_balances,
coins(INITIAL_BALANCE + MINT_PRICE - MINT_FEE, NATIVE_DENOM)
);
let buyer_balances = router.wrap().query_all_balances(buyer.clone()).unwrap();
assert_eq!(
buyer_balances,
coins(INITIAL_BALANCE - MINT_PRICE, NATIVE_DENOM)
);
let res: MintCountResponse = router
.wrap()
.query_wasm_smart(
minter_addr.clone(),
&QueryMsg::MintCount {
address: buyer.to_string(),
},
)
.unwrap();
assert_eq!(res.count, 1);
assert_eq!(res.address, buyer.to_string());
let query_owner_msg = Cw721QueryMsg::OwnerOf {
token_id: String::from("1"),
include_expired: None,
};
let res: OwnerOfResponse = router
.wrap()
.query_wasm_smart(config.sg721_address.clone(), &query_owner_msg)
.unwrap();
assert_eq!(res.owner, buyer.to_string());
let mint_to_msg = ExecuteMsg::MintTo {
recipient: buyer.to_string(),
};
let res = router.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_to_msg,
&coins_for_msg(Coin {
amount: Uint128::from(ADMIN_MINT_PRICE),
denom: NATIVE_DENOM.to_string(),
}),
);
assert!(res.is_err());
let res = router.execute_contract(
creator.clone(),
minter_addr.clone(),
&mint_to_msg,
&coins_for_msg(Coin {
amount: Uint128::from(ADMIN_MINT_PRICE),
denom: NATIVE_DENOM.to_string(),
}),
);
assert!(res.is_ok());
let res: MintCountResponse = router
.wrap()
.query_wasm_smart(
minter_addr.clone(),
&QueryMsg::MintCount {
address: buyer.to_string(),
},
)
.unwrap();
assert_eq!(res.count, 1);
assert_eq!(res.address, buyer.to_string());
let minter_balance = router
.wrap()
.query_all_balances(minter_addr.clone())
.unwrap();
assert_eq!(0, minter_balance.len());
let query_owner_msg = Cw721QueryMsg::OwnerOf {
token_id: String::from("1"),
include_expired: None,
};
let res: OwnerOfResponse = router
.wrap()
.query_wasm_smart(config.sg721_address, &query_owner_msg)
.unwrap();
assert_eq!(res.owner, buyer.to_string());
let mint_msg = ExecuteMsg::Mint {};
let res = router.execute_contract(
buyer,
minter_addr.clone(),
&mint_msg,
&coins_for_msg(Coin {
amount: Uint128::from(MINT_PRICE),
denom: NATIVE_DENOM.to_string(),
}),
);
assert!(res.is_err());
let res = router.execute_contract(
creator,
minter_addr,
&mint_to_msg,
&coins_for_msg(Coin {
amount: Uint128::from(ADMIN_MINT_PRICE),
denom: NATIVE_DENOM.to_string(),
}),
);
assert!(res.is_err());
}
#[test]
fn mint_count_query() {
let mut router = custom_mock_app();
let (creator, buyer) = setup_accounts(&mut router);
let num_tokens = 10;
let (minter_addr, config) = setup_minter_contract(&mut router, &creator, num_tokens);
let sg721_addr = Addr::unchecked(config.sg721_address);
let whitelist_addr = setup_whitelist_contract(&mut router, &creator);
const EXPIRATION_TIME: Timestamp = Timestamp::from_nanos(GENESIS_MINT_START_TIME + 10_000);
setup_block_time(&mut router, GENESIS_MINT_START_TIME - 1000, None);
let wl_msg = WhitelistExecuteMsg::UpdateEndTime(EXPIRATION_TIME);
let res = router.execute_contract(creator.clone(), whitelist_addr.clone(), &wl_msg, &[]);
assert!(res.is_ok());
let wl_msg = WhitelistExecuteMsg::UpdateStartTime(Timestamp::from_nanos(0));
let res = router.execute_contract(creator.clone(), whitelist_addr.clone(), &wl_msg, &[]);
assert!(res.is_ok());
let set_whitelist_msg = ExecuteMsg::SetWhitelist {
whitelist: whitelist_addr.to_string(),
};
let res = router.execute_contract(
creator.clone(),
minter_addr.clone(),
&set_whitelist_msg,
&[],
);
assert!(res.is_ok());
let set_whitelist_msg = ExecuteMsg::UpdatePerAddressLimit {
per_address_limit: 3,
};
let res = router.execute_contract(
creator.clone(),
minter_addr.clone(),
&set_whitelist_msg,
&[],
);
assert!(res.is_ok());
let inner_msg = AddMembersMsg {
to_add: vec![buyer.to_string()],
};
let wasm_msg = WhitelistExecuteMsg::AddMembers(inner_msg);
let res = router.execute_contract(creator.clone(), whitelist_addr, &wasm_msg, &[]);
assert!(res.is_ok());
setup_block_time(&mut router, GENESIS_MINT_START_TIME, Some(10));
let mint_msg = ExecuteMsg::Mint {};
let res = router.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_msg,
&coins(WHITELIST_AMOUNT, NATIVE_DENOM),
);
assert!(res.is_ok());
let res: MintCountResponse = router
.wrap()
.query_wasm_smart(
minter_addr.clone(),
&QueryMsg::MintCount {
address: buyer.to_string(),
},
)
.unwrap();
assert_eq!(res.count, 1);
assert_eq!(res.address, buyer.to_string());
let mint_msg = ExecuteMsg::Mint {};
let err = router
.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_msg,
&coins(WHITELIST_AMOUNT, NATIVE_DENOM),
)
.unwrap_err();
assert_eq!(
err.source().unwrap().to_string(),
ContractError::MaxPerAddressLimitExceeded {}.to_string()
);
setup_block_time(&mut router, GENESIS_MINT_START_TIME + 20_000, Some(11));
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());
let res: MintCountResponse = router
.wrap()
.query_wasm_smart(
minter_addr.clone(),
&QueryMsg::MintCount {
address: buyer.to_string(),
},
)
.unwrap();
assert_eq!(res.count, 2);
assert_eq!(res.address, buyer.to_string());
let tokens_msg = Cw721QueryMsg::Tokens {
owner: buyer.to_string(),
start_after: None,
limit: None,
};
let res: TokensResponse = router
.wrap()
.query_wasm_smart(sg721_addr.clone(), &tokens_msg)
.unwrap();
let sold_token_id: u32 = res.tokens[1].parse::<u32>().unwrap();
let transfer_msg: Cw721ExecuteMsg<Empty> = Cw721ExecuteMsg::TransferNft {
recipient: creator.to_string(),
token_id: sold_token_id.to_string(),
};
let res = router.execute_contract(
buyer.clone(),
sg721_addr,
&transfer_msg,
&coins_for_msg(coin(123, NATIVE_DENOM)),
);
assert!(res.is_ok());
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());
let res: MintCountResponse = router
.wrap()
.query_wasm_smart(
minter_addr.clone(),
&QueryMsg::MintCount {
address: buyer.to_string(),
},
)
.unwrap();
assert_eq!(res.count, 3);
assert_eq!(res.address, buyer.to_string());
let mint_msg = ExecuteMsg::Mint {};
let err = router
.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_msg,
&coins(WHITELIST_AMOUNT, NATIVE_DENOM),
)
.unwrap_err();
assert_eq!(
err.source().unwrap().to_string(),
ContractError::MaxPerAddressLimitExceeded {}.to_string()
);
let res: MintCountResponse = router
.wrap()
.query_wasm_smart(
minter_addr,
&QueryMsg::MintCount {
address: buyer.to_string(),
},
)
.unwrap();
assert_eq!(res.count, 3);
assert_eq!(res.address, buyer.to_string());
}
#[test]
fn whitelist_already_started() {
let mut router = custom_mock_app();
let (creator, _) = setup_accounts(&mut router);
let num_tokens = 1;
let (minter_addr, _) = setup_minter_contract(&mut router, &creator, num_tokens);
let whitelist_addr = setup_whitelist_contract(&mut router, &creator);
setup_block_time(&mut router, GENESIS_MINT_START_TIME + 101, None);
let set_whitelist_msg = ExecuteMsg::SetWhitelist {
whitelist: whitelist_addr.to_string(),
};
router
.execute_contract(
creator.clone(),
minter_addr,
&set_whitelist_msg,
&coins(MINT_PRICE, NATIVE_DENOM),
)
.unwrap_err();
}
#[test]
fn whitelist_can_update_before_start() {
let mut router = custom_mock_app();
let (creator, _) = setup_accounts(&mut router);
let num_tokens = 1;
let (minter_addr, _) = setup_minter_contract(&mut router, &creator, num_tokens);
let whitelist_addr = setup_whitelist_contract(&mut router, &creator);
setup_block_time(&mut router, GENESIS_MINT_START_TIME - 1000, None);
let set_whitelist_msg = ExecuteMsg::SetWhitelist {
whitelist: whitelist_addr.to_string(),
};
router
.execute_contract(
creator.clone(),
minter_addr.clone(),
&set_whitelist_msg,
&[],
)
.unwrap();
router
.execute_contract(creator.clone(), minter_addr, &set_whitelist_msg, &[])
.unwrap();
}
#[test]
fn whitelist_access_len_add_remove_expiration() {
let mut router = custom_mock_app();
let (creator, buyer) = setup_accounts(&mut router);
let num_tokens = 1;
let (minter_addr, config) = setup_minter_contract(&mut router, &creator, num_tokens);
let sg721_addr = config.sg721_address;
let whitelist_addr = setup_whitelist_contract(&mut router, &creator);
const AFTER_GENESIS_TIME: Timestamp = Timestamp::from_nanos(GENESIS_MINT_START_TIME + 100);
setup_block_time(&mut router, GENESIS_MINT_START_TIME - 10, None);
let wl_msg = WhitelistExecuteMsg::UpdateEndTime(AFTER_GENESIS_TIME);
router
.execute_contract(buyer.clone(), whitelist_addr.clone(), &wl_msg, &[])
.unwrap_err();
let wl_msg = WhitelistExecuteMsg::UpdateEndTime(AFTER_GENESIS_TIME);
let res = router.execute_contract(creator.clone(), whitelist_addr.clone(), &wl_msg, &[]);
assert!(res.is_ok());
let wl_msg = WhitelistExecuteMsg::UpdateStartTime(Timestamp::from_nanos(0));
let res = router.execute_contract(creator.clone(), whitelist_addr.clone(), &wl_msg, &[]);
assert!(res.is_ok());
let set_whitelist_msg = ExecuteMsg::SetWhitelist {
whitelist: whitelist_addr.to_string(),
};
let res = router.execute_contract(
creator.clone(),
minter_addr.clone(),
&set_whitelist_msg,
&[],
);
assert!(res.is_ok());
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_err());
let inner_msg = AddMembersMsg {
to_add: vec![buyer.to_string()],
};
let wasm_msg = WhitelistExecuteMsg::AddMembers(inner_msg);
let res = router.execute_contract(creator.clone(), whitelist_addr.clone(), &wasm_msg, &[]);
assert!(res.is_ok());
let mint_msg = ExecuteMsg::Mint {};
router
.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_msg,
&coins(MINT_PRICE, NATIVE_DENOM),
)
.unwrap_err();
setup_block_time(&mut router, GENESIS_MINT_START_TIME, None);
let mint_price_response: MintPriceResponse = router
.wrap()
.query_wasm_smart(minter_addr.clone(), &QueryMsg::MintPrice {})
.unwrap();
assert_eq!(
coin(WHITELIST_AMOUNT, NATIVE_DENOM),
mint_price_response.whitelist_price.unwrap()
);
assert_eq!(
coin(WHITELIST_AMOUNT, NATIVE_DENOM),
mint_price_response.current_price
);
assert_eq!(
coin(MINT_PRICE, NATIVE_DENOM),
mint_price_response.public_price
);
let mint_msg = ExecuteMsg::Mint {};
let res = router.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_msg,
&coins(WHITELIST_AMOUNT, NATIVE_DENOM),
);
assert!(res.is_ok());
let mint_msg = ExecuteMsg::Mint {};
let err = router
.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_msg,
&coins(WHITELIST_AMOUNT, NATIVE_DENOM),
)
.unwrap_err();
assert_eq!(
err.source().unwrap().to_string(),
ContractError::MaxPerAddressLimitExceeded {}.to_string()
);
let transfer_msg: Cw721ExecuteMsg<Empty> = Cw721ExecuteMsg::TransferNft {
recipient: creator.to_string(),
token_id: "1".to_string(),
};
let res = router.execute_contract(
buyer.clone(),
Addr::unchecked(sg721_addr),
&transfer_msg,
&coins_for_msg(coin(123, NATIVE_DENOM)),
);
assert!(res.is_ok());
let mint_msg = ExecuteMsg::Mint {};
let err = router
.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_msg,
&coins(WHITELIST_AMOUNT, NATIVE_DENOM),
)
.unwrap_err();
assert_eq!(
err.source().unwrap().to_string(),
ContractError::MaxPerAddressLimitExceeded {}.to_string()
);
let inner_msg = AddMembersMsg { to_add: vec![] };
let wasm_msg = WhitelistExecuteMsg::AddMembers(inner_msg);
let res = router.execute_contract(creator.clone(), whitelist_addr, &wasm_msg, &[]);
assert!(res.is_ok());
let mint_msg = ExecuteMsg::Mint {};
let res = router.execute_contract(
buyer,
minter_addr,
&mint_msg,
&coins(MINT_PRICE, NATIVE_DENOM),
);
assert!(res.is_err());
}
#[test]
fn before_start_time() {
let mut router = custom_mock_app();
let (creator, buyer) = setup_accounts(&mut router);
let num_tokens = 1;
let (minter_addr, _) = setup_minter_contract(&mut router, &creator, num_tokens);
setup_block_time(&mut router, GENESIS_MINT_START_TIME - 10, None);
let start_time_msg = ExecuteMsg::UpdateStartTime(Timestamp::from_nanos(0));
let res = router.execute_contract(
buyer.clone(),
minter_addr.clone(),
&start_time_msg,
&coins(MINT_PRICE, NATIVE_DENOM),
);
assert!(res.is_err());
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_err());
let start_time_response: StartTimeResponse = router
.wrap()
.query_wasm_smart(minter_addr.clone(), &QueryMsg::StartTime {})
.unwrap();
assert_eq!(
Timestamp::from_nanos(GENESIS_MINT_START_TIME).to_string(),
start_time_response.start_time
);
setup_block_time(&mut router, GENESIS_MINT_START_TIME + 10_000_000, None);
let mint_msg = ExecuteMsg::Mint {};
let res = router.execute_contract(
buyer,
minter_addr,
&mint_msg,
&coins(MINT_PRICE, NATIVE_DENOM),
);
assert!(res.is_ok());
}
#[test]
fn check_per_address_limit() {
let mut router = custom_mock_app();
let (creator, buyer) = setup_accounts(&mut router);
let num_tokens = 2;
let (minter_addr, _config) = setup_minter_contract(&mut router, &creator, num_tokens);
setup_block_time(&mut router, GENESIS_MINT_START_TIME, None);
let per_address_limit_msg = ExecuteMsg::UpdatePerAddressLimit {
per_address_limit: 30,
};
let res = router.execute_contract(
buyer.clone(),
minter_addr.clone(),
&per_address_limit_msg,
&coins(MINT_PRICE, NATIVE_DENOM),
);
assert!(res.is_err());
let per_address_limit_msg = ExecuteMsg::UpdatePerAddressLimit {
per_address_limit: 0,
};
let res = router.execute_contract(
creator.clone(),
minter_addr.clone(),
&per_address_limit_msg,
&coins(MINT_PRICE, NATIVE_DENOM),
);
assert!(res.is_err());
let per_address_limit_msg = ExecuteMsg::UpdatePerAddressLimit {
per_address_limit: 100,
};
let res = router.execute_contract(
creator.clone(),
minter_addr.clone(),
&per_address_limit_msg,
&coins(MINT_PRICE, NATIVE_DENOM),
);
assert!(res.is_err());
let per_address_limit_msg = ExecuteMsg::UpdatePerAddressLimit {
per_address_limit: 1,
};
let res = router.execute_contract(
creator.clone(),
minter_addr.clone(),
&per_address_limit_msg,
&[],
);
assert!(res.is_ok());
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());
let mint_msg = ExecuteMsg::Mint {};
let res = router.execute_contract(
buyer,
minter_addr,
&mint_msg,
&coins(MINT_PRICE, NATIVE_DENOM),
);
assert!(res.is_err());
}
#[test]
fn check_dynamic_per_address_limit() {
let mut router = custom_mock_app();
setup_block_time(&mut router, GENESIS_MINT_START_TIME - 1, None);
let (creator, _) = setup_accounts(&mut router);
let num_tokens = 400;
let minter_code_id = router.store_code(contract_minter());
let creation_fee = coins(CREATION_FEE, NATIVE_DENOM);
let factory_code_id = router.store_code(contract_factory());
let mut params = mock_params();
params.code_id = minter_code_id;
let factory_addr = router
.instantiate_contract(
factory_code_id,
creator.clone(),
&vending_factory::msg::InstantiateMsg { params },
&[],
"factory",
None,
)
.unwrap();
let sg721_code_id = router.store_code(contract_sg721());
let mut msg = mock_create_minter();
msg.init_msg.mint_price = coin(MINT_PRICE, NATIVE_DENOM);
msg.init_msg.num_tokens = num_tokens;
msg.collection_params.code_id = sg721_code_id;
msg.collection_params.info.creator = creator.to_string();
let msg = Sg2ExecuteMsg::CreateMinter(msg);
let err = router
.execute_contract(creator.clone(), factory_addr.clone(), &msg, &creation_fee)
.unwrap_err();
assert_eq!(
err.source().unwrap().source().unwrap().to_string(),
ContractError::InvalidPerAddressLimit {
max: num_tokens / 100,
min: 1,
got: mock_create_minter().init_msg.per_address_limit,
}
.to_string()
);
let num_tokens = 1000;
let mut msg = mock_create_minter();
msg.init_msg.mint_price = coin(MINT_PRICE, NATIVE_DENOM);
msg.init_msg.num_tokens = num_tokens;
msg.collection_params.code_id = sg721_code_id;
msg.collection_params.info.creator = creator.to_string();
let msg = Sg2ExecuteMsg::CreateMinter(msg);
let res = router.execute_contract(creator.clone(), factory_addr, &msg, &creation_fee);
assert!(res.is_ok());
let minter_addr = Addr::unchecked("contract1");
let update_msg = ExecuteMsg::UpdatePerAddressLimit {
per_address_limit: 11,
};
let err = router
.execute_contract(creator, minter_addr, &update_msg, &[])
.unwrap_err();
assert_eq!(
err.source().unwrap().to_string(),
ContractError::InvalidPerAddressLimit {
max: num_tokens / 100,
min: 1,
got: 11,
}
.to_string()
);
}
#[test]
fn mint_for_token_id_addr() {
let mut router = custom_mock_app();
let (creator, buyer) = setup_accounts(&mut router);
let num_tokens = 4;
let (minter_addr, config) = setup_minter_contract(&mut router, &creator, num_tokens);
setup_block_time(&mut router, GENESIS_MINT_START_TIME, None);
let mint_for_msg = ExecuteMsg::MintFor {
token_id: 1,
recipient: buyer.to_string(),
};
let err = router
.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_for_msg,
&coins_for_msg(Coin {
amount: Uint128::from(ADMIN_MINT_PRICE),
denom: NATIVE_DENOM.to_string(),
}),
)
.unwrap_err();
assert_eq!(
err.source().unwrap().to_string(),
ContractError::Unauthorized("Sender is not an admin".to_string()).to_string(),
);
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());
let tokens_msg = Cw721QueryMsg::Tokens {
owner: buyer.to_string(),
start_after: None,
limit: None,
};
let res: TokensResponse = router
.wrap()
.query_wasm_smart(config.sg721_address.clone(), &tokens_msg)
.unwrap();
let sold_token_id: u32 = res.tokens[0].parse::<u32>().unwrap();
let minter_balance = router
.wrap()
.query_all_balances(minter_addr.clone())
.unwrap();
assert_eq!(0, minter_balance.len());
let token_id: u32 = 0;
let mint_for_msg = ExecuteMsg::MintFor {
token_id,
recipient: buyer.to_string(),
};
let err = router
.execute_contract(
creator.clone(),
minter_addr.clone(),
&mint_for_msg,
&coins_for_msg(Coin {
amount: Uint128::from(ADMIN_MINT_PRICE),
denom: NATIVE_DENOM.to_string(),
}),
)
.unwrap_err();
assert_eq!(
ContractError::InvalidTokenId {}.to_string(),
err.source().unwrap().to_string()
);
let mint_for_msg = ExecuteMsg::MintFor {
token_id: sold_token_id,
recipient: buyer.to_string(),
};
let err = router
.execute_contract(
creator.clone(),
minter_addr.clone(),
&mint_for_msg,
&coins_for_msg(Coin {
amount: Uint128::from(ADMIN_MINT_PRICE),
denom: NATIVE_DENOM.to_string(),
}),
)
.unwrap_err();
assert_eq!(
ContractError::TokenIdAlreadySold {
token_id: sold_token_id
}
.to_string(),
err.source().unwrap().to_string()
);
let mintable_num_tokens_response: MintableNumTokensResponse = router
.wrap()
.query_wasm_smart(minter_addr.clone(), &QueryMsg::MintableNumTokens {})
.unwrap();
assert_eq!(mintable_num_tokens_response.count, 3);
let err = router
.execute_contract(
creator.clone(),
minter_addr.clone(),
&mint_for_msg,
&coins_for_msg(Coin {
amount: Uint128::from(ADMIN_MINT_PRICE - 1),
denom: NATIVE_DENOM.to_string(),
}),
)
.unwrap_err();
assert_eq!(
ContractError::IncorrectPaymentAmount(
coin(ADMIN_MINT_PRICE - 1, NATIVE_DENOM.to_string()),
coin(ADMIN_MINT_PRICE, NATIVE_DENOM.to_string())
)
.to_string(),
err.source().unwrap().to_string()
);
let token_id = 2;
let mint_for_msg = ExecuteMsg::MintFor {
token_id,
recipient: buyer.to_string(),
};
let res = router.execute_contract(
creator.clone(),
minter_addr.clone(),
&mint_for_msg,
&coins_for_msg(Coin {
amount: Uint128::from(ADMIN_MINT_PRICE),
denom: NATIVE_DENOM.to_string(),
}),
);
assert!(res.is_ok());
let res: OwnerOfResponse = router
.wrap()
.query_wasm_smart(
config.sg721_address,
&Cw721QueryMsg::OwnerOf {
token_id: 2.to_string(),
include_expired: None,
},
)
.unwrap();
assert_eq!(res.owner, buyer.to_string());
let mintable_num_tokens_response: MintableNumTokensResponse = router
.wrap()
.query_wasm_smart(minter_addr, &QueryMsg::MintableNumTokens {})
.unwrap();
assert_eq!(mintable_num_tokens_response.count, 2);
}
#[test]
fn test_update_start_time() {
let mut router = custom_mock_app();
let (creator, _) = setup_accounts(&mut router);
let num_tokens = 10;
let (minter_addr, _) = setup_minter_contract(&mut router, &creator, num_tokens);
setup_block_time(&mut router, GENESIS_MINT_START_TIME + 100, None);
let msg = ExecuteMsg::UpdateStartTime(Timestamp::from_nanos(GENESIS_MINT_START_TIME - 1000));
let err = router
.execute_contract(creator, minter_addr, &msg, &[])
.unwrap_err();
assert_eq!(
err.source().unwrap().to_string(),
ContractError::AlreadyStarted {}.to_string(),
);
}
#[test]
fn test_invalid_start_time() {
let mut router = custom_mock_app();
let (creator, _) = setup_accounts(&mut router);
let sg721_code_id = router.store_code(contract_sg721());
let minter_code_id = router.store_code(contract_minter());
let creation_fee = coins(CREATION_FEE, NATIVE_DENOM);
let factory_code_id = router.store_code(contract_factory());
let mut params = mock_params();
params.code_id = minter_code_id;
let factory_addr = router
.instantiate_contract(
factory_code_id,
creator.clone(),
&vending_factory::msg::InstantiateMsg { params },
&[],
"factory",
None,
)
.unwrap();
setup_block_time(&mut router, GENESIS_MINT_START_TIME - 1000, None);
let mut minter_msg = mock_create_minter();
minter_msg.init_msg.num_tokens = 10;
minter_msg.collection_params.code_id = sg721_code_id;
minter_msg.init_msg.start_time = Timestamp::from_nanos(GENESIS_MINT_START_TIME - 100);
let msg = Sg2ExecuteMsg::CreateMinter(minter_msg.clone());
router
.execute_contract(creator.clone(), factory_addr.clone(), &msg, &creation_fee)
.unwrap_err();
setup_block_time(&mut router, GENESIS_MINT_START_TIME + 1000, None);
minter_msg.init_msg.start_time = Timestamp::from_nanos(GENESIS_MINT_START_TIME + 500);
let msg = Sg2ExecuteMsg::CreateMinter(minter_msg.clone());
router
.execute_contract(creator.clone(), factory_addr.clone(), &msg, &creation_fee)
.unwrap_err();
setup_block_time(&mut router, GENESIS_MINT_START_TIME + 400, None);
minter_msg.init_msg.start_time = Timestamp::from_nanos(GENESIS_MINT_START_TIME + 500);
let msg = Sg2ExecuteMsg::CreateMinter(minter_msg);
router
.execute_contract(creator.clone(), factory_addr, &msg, &creation_fee)
.unwrap();
let minter_addr = Addr::unchecked("contract1");
let msg = ExecuteMsg::UpdateStartTime(Timestamp::from_nanos(GENESIS_MINT_START_TIME - 100));
let res = router.execute_contract(creator.clone(), minter_addr.clone(), &msg, &[]);
assert!(res.is_err());
let msg = ExecuteMsg::UpdateStartTime(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 300));
let res = router.execute_contract(creator.clone(), minter_addr.clone(), &msg, &[]);
assert!(res.is_err());
let msg = ExecuteMsg::UpdateStartTime(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 450));
let res = router.execute_contract(creator.clone(), minter_addr.clone(), &msg, &[]);
assert!(res.is_ok());
setup_block_time(&mut router, GENESIS_MINT_START_TIME + 500, None);
let msg = ExecuteMsg::UpdateStartTime(Timestamp::from_nanos(GENESIS_MINT_START_TIME + 450));
let err = router
.execute_contract(creator, minter_addr, &msg, &[])
.unwrap_err();
assert_eq!(err.source().unwrap().to_string(), "AlreadyStarted");
}
#[test]
fn unhappy_path() {
let mut router = custom_mock_app();
let (creator, buyer) = setup_accounts(&mut router);
let num_tokens = 1;
let (minter_addr, _config) = setup_minter_contract(&mut router, &creator, num_tokens);
let mint_msg = ExecuteMsg::Mint {};
let res = router.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_msg,
&coins(1, NATIVE_DENOM),
);
assert!(res.is_err());
let mint_msg = ExecuteMsg::Mint {};
let res = router.execute_contract(
buyer.clone(),
minter_addr.clone(),
&mint_msg,
&coins(11111, NATIVE_DENOM),
);
assert!(res.is_err());
let mint_msg = ExecuteMsg::Mint {};
let res = router.execute_contract(buyer, minter_addr, &mint_msg, &coins(MINT_PRICE, "uatom"));
assert!(res.is_err());
}
#[test]
fn update_mint_price() {
let mut router = custom_mock_app();
let (creator, buyer) = setup_accounts(&mut router);
let num_tokens = 1;
let (minter_addr, _) = setup_minter_contract(&mut router, &creator, num_tokens);
setup_block_time(&mut router, GENESIS_MINT_START_TIME - 10, None);
let update_msg = ExecuteMsg::UpdateMintPrice {
price: MINT_PRICE + 1,
};
let res = router.execute_contract(creator.clone(), minter_addr.clone(), &update_msg, &[]);
assert!(res.is_ok());
let update_msg = ExecuteMsg::UpdateMintPrice {
price: MINT_PRICE - 2,
};
let res = router.execute_contract(creator.clone(), minter_addr.clone(), &update_msg, &[]);
assert!(res.is_ok());
let update_msg = ExecuteMsg::UpdateMintPrice {
price: MINT_PRICE - 1,
};
let res = router.execute_contract(creator.clone(), minter_addr.clone(), &update_msg, &[]);
assert!(res.is_ok());
setup_block_time(&mut router, GENESIS_MINT_START_TIME + 10_000_000, None);
let update_msg = ExecuteMsg::UpdateMintPrice { price: MINT_PRICE };
let err = router
.execute_contract(creator.clone(), minter_addr.clone(), &update_msg, &[])
.unwrap_err();
assert_eq!(
err.source().unwrap().to_string(),
ContractError::UpdatedMintPriceTooHigh {
allowed: MINT_PRICE - 1,
updated: MINT_PRICE
}
.to_string()
);
let update_msg = ExecuteMsg::UpdateMintPrice {
price: MINT_PRICE - 2,
};
let res = router.execute_contract(creator.clone(), minter_addr.clone(), &update_msg, &[]);
assert!(res.is_ok());
let mint_msg = ExecuteMsg::Mint {};
let res = router.execute_contract(
buyer,
minter_addr,
&mint_msg,
&coins(MINT_PRICE - 2, NATIVE_DENOM),
);
assert!(res.is_ok());
}