1#[cfg(not(feature = "library"))]
2use cosmwasm_std::entry_point;
3use cosmwasm_std::{
4 coins, from_binary, to_binary, Addr, Attribute, BankMsg, Binary, Coin, CosmosMsg, Deps,
5 DepsMut, Env, MessageInfo, Order, Reply, ReplyOn, Response, StdError, StdResult, SubMsg,
6 Timestamp, Uint128, WasmMsg,
7};
8use cw2::{get_contract_version, set_contract_version};
9use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg};
10use cw_storage_plus::Bound;
11use cw_utils::parse_reply_instantiate_data;
12
13use komple_framework_permission_module::msg::ExecuteMsg as PermissionExecuteMsg;
14use komple_framework_token_module::{
15 helper::KompleTokenModule,
16 msg::{InstantiateMsg as TokenInstantiateMsg, MetadataInfo, TokenInfo},
17 state::CollectionConfig,
18};
19use komple_framework_types::modules::fee::{FundInfo, MintFees};
20use komple_framework_types::modules::whitelist::WHITELIST_NAMESPACE;
21use komple_framework_types::modules::Modules;
22use komple_framework_types::shared::query::ResponseWrapper;
23use komple_framework_types::shared::RegisterMsg;
24use komple_framework_utils::{
25 check_admin_privileges,
26 funds::{check_cw20_fund_info, FundsError},
27 response::ResponseHelper,
28 shared::{execute_lock_execute, execute_update_operators},
29 storage::StorageHelper,
30};
31use komple_framework_utils::{funds::check_single_coin, response::EventHelper};
32use komple_framework_whitelist_module::helper::KompleWhitelistHelper;
33use semver::Version;
34
35use crate::{error::ContractError, msg::ReceiveMsg, state::EXECUTE_LOCK};
36use crate::{
37 msg::CollectionFundInfo,
38 state::{
39 CollectionInfo, Config, BLACKLIST_COLLECTION_ADDRS, COLLECTION_ADDRS, COLLECTION_FUND_INFO,
40 COLLECTION_ID, COLLECTION_INFO, CONFIG, HUB_ADDR, LINKED_COLLECTIONS, MINT_LOCKS,
41 OPERATORS,
42 },
43};
44use crate::{
45 msg::{CollectionsResponse, ExecuteMsg, MigrateMsg, MintMsg, QueryMsg},
46 state::CREATORS,
47};
48
49const CONTRACT_NAME: &str = "crates.io:komple-framework-mint-module";
51const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
52
53const TOKEN_INSTANTIATE_REPLY_ID: u64 = 1;
54
55#[cfg_attr(not(feature = "library"), entry_point)]
56pub fn instantiate(
57 deps: DepsMut,
58 _env: Env,
59 info: MessageInfo,
60 msg: RegisterMsg,
61) -> Result<Response, ContractError> {
62 set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
63
64 let admin = deps.api.addr_validate(&msg.admin)?;
65
66 let config = Config {
67 admin,
68 public_collection_creation: false,
69 mint_lock: false,
70 };
71 CONFIG.save(deps.storage, &config)?;
72
73 COLLECTION_ID.save(deps.storage, &0)?;
74
75 HUB_ADDR.save(deps.storage, &info.sender)?;
76
77 EXECUTE_LOCK.save(deps.storage, &false)?;
78
79 Ok(ResponseHelper::new_module("mint", "instantiate").add_event(
80 EventHelper::new("mint_instantiate")
81 .add_attribute("admin", config.admin)
82 .add_attribute(
83 "public_collection_creation",
84 config.public_collection_creation.to_string(),
85 )
86 .add_attribute("mint_lock", config.mint_lock.to_string())
87 .get(),
88 ))
89}
90
91#[cfg_attr(not(feature = "library"), entry_point)]
92pub fn execute(
93 deps: DepsMut,
94 env: Env,
95 info: MessageInfo,
96 msg: ExecuteMsg,
97) -> Result<Response, ContractError> {
98 let execute_lock = EXECUTE_LOCK.load(deps.storage)?;
99 if execute_lock {
100 return Err(ContractError::ExecuteLocked {});
101 };
102
103 match msg {
104 ExecuteMsg::CreateCollection {
105 code_id,
106 collection_config,
107 collection_info,
108 metadata_info,
109 token_info,
110 fund_info,
111 linked_collections,
112 } => execute_create_collection(
113 deps,
114 env,
115 info,
116 code_id,
117 collection_config,
118 collection_info,
119 metadata_info,
120 token_info,
121 fund_info,
122 linked_collections,
123 ),
124 ExecuteMsg::UpdatePublicCollectionCreation {
125 public_collection_creation,
126 } => execute_update_public_collection_creation(deps, env, info, public_collection_creation),
127 ExecuteMsg::UpdateCollectionMintLock {
128 collection_id,
129 lock,
130 } => execute_update_collection_mint_lock(deps, env, info, collection_id, lock),
131 ExecuteMsg::Mint {
132 collection_id,
133 metadata_id,
134 } => execute_mint(deps, env, info, collection_id, metadata_id),
135 ExecuteMsg::AdminMint {
136 collection_id,
137 recipient,
138 metadata_id,
139 } => execute_admin_mint(deps, env, info, collection_id, recipient, metadata_id),
140 ExecuteMsg::PermissionMint {
141 permission_msg,
142 mint_msg,
143 } => execute_permission_mint(deps, env, info, permission_msg, mint_msg),
144 ExecuteMsg::UpdateLinkedCollections {
145 collection_id,
146 linked_collections,
147 } => execute_update_linked_collections(deps, env, info, collection_id, linked_collections),
148 ExecuteMsg::UpdateCreators { addrs } => execute_update_creators(deps, env, info, addrs),
149 ExecuteMsg::UpdateCollectionStatus {
150 collection_id,
151 is_blacklist,
152 } => execute_update_collection_status(deps, env, info, collection_id, is_blacklist),
153 ExecuteMsg::UpdateOperators { addrs } => {
154 let config = CONFIG.load(deps.storage)?;
155 let res = execute_update_operators(
156 deps,
157 info,
158 "mint",
159 &env.contract.address,
160 &config.admin,
161 OPERATORS,
162 addrs,
163 );
164 match res {
165 Ok(res) => Ok(res),
166 Err(err) => Err(err.into()),
167 }
168 }
169 ExecuteMsg::LockExecute {} => {
170 let res = execute_lock_execute(deps, info, "mint", &env.contract.address, EXECUTE_LOCK);
171 match res {
172 Ok(res) => Ok(res),
173 Err(err) => Err(err.into()),
174 }
175 }
176 ExecuteMsg::Receive(msg) => execute_receive(deps, env, info, msg),
177 }
178}
179
180pub fn execute_create_collection(
181 deps: DepsMut,
182 env: Env,
183 info: MessageInfo,
184 code_id: u64,
185 collection_config: CollectionConfig,
186 collection_info: CollectionInfo,
187 metadata_info: MetadataInfo,
188 mut token_info: TokenInfo,
189 fund_info: CollectionFundInfo,
190 linked_collections: Option<Vec<u32>>,
191) -> Result<Response, ContractError> {
192 let config = CONFIG.load(deps.storage)?;
193
194 if !config.public_collection_creation {
196 let hub_addr = HUB_ADDR.may_load(deps.storage)?;
197 let operators = OPERATORS.may_load(deps.storage)?;
198
199 let creators = CREATORS.may_load(deps.storage)?;
201 match creators {
202 Some(creators) => {
203 if !creators.contains(&info.sender) {
205 check_admin_privileges(
206 &info.sender,
207 &env.contract.address,
208 &config.admin,
209 hub_addr,
210 operators,
211 )?;
212 }
213 }
214 None => {
216 check_admin_privileges(
217 &info.sender,
218 &env.contract.address,
219 &config.admin,
220 hub_addr,
221 operators,
222 )?;
223 }
224 }
225 };
226
227 token_info.minter = env.contract.address.to_string();
228 let token_instantiate_msg = TokenInstantiateMsg {
229 creator: info.sender.to_string(),
230 collection_config: collection_config.clone(),
231 collection_type: collection_info.clone().collection_type,
232 collection_name: collection_info.clone().name,
233 metadata_info,
234 token_info,
235 };
236 let register_msg = RegisterMsg {
237 admin: config.admin.to_string(),
238 data: Some(to_binary(&token_instantiate_msg)?),
239 };
240
241 let contract_info = deps
242 .querier
243 .query_wasm_contract_info(env.contract.address)?;
244 let sub_msg: SubMsg = SubMsg {
245 msg: WasmMsg::Instantiate {
246 code_id,
247 msg: to_binary(®ister_msg)?,
248 funds: info.funds,
249 admin: contract_info.admin,
250 label: String::from("Komple Framework Token Module"),
251 }
252 .into(),
253 id: TOKEN_INSTANTIATE_REPLY_ID,
254 gas_limit: None,
255 reply_on: ReplyOn::Success,
256 };
257
258 let collection_id = (COLLECTION_ID.load(deps.storage)?) + 1;
259
260 if linked_collections.is_some() {
261 check_collection_ids_exists(&deps, &linked_collections.clone().unwrap())?;
262 LINKED_COLLECTIONS.save(deps.storage, collection_id, &linked_collections.unwrap())?;
263 }
264
265 COLLECTION_ID.save(deps.storage, &collection_id)?;
266
267 COLLECTION_INFO.save(deps.storage, collection_id, &collection_info)?;
268
269 MINT_LOCKS.save(deps.storage, collection_id, &false)?;
270
271 let cw20_address = match fund_info.cw20_address {
272 Some(addr) => Some(deps.api.addr_validate(&addr)?),
273 None => None,
274 };
275 let fund_info = FundInfo {
276 is_native: fund_info.is_native,
277 denom: fund_info.denom,
278 cw20_address,
279 };
280
281 if !fund_info.is_native {
282 check_cw20_fund_info(&deps, &fund_info)?;
283 };
284 COLLECTION_FUND_INFO.save(deps.storage, collection_id, &fund_info)?;
285
286 Ok(ResponseHelper::new_module("mint", "create_collection")
287 .add_submessage(sub_msg)
288 .add_event(
289 EventHelper::new("mint_create_collection")
290 .add_attribute("creator", token_instantiate_msg.creator)
291 .add_attribute("minter", token_instantiate_msg.token_info.minter)
292 .add_attribute("symbol", token_instantiate_msg.token_info.symbol)
293 .add_attribute(
294 "collection_type",
295 token_instantiate_msg.collection_type.to_string(),
296 )
297 .add_attribute("collection_name", token_instantiate_msg.collection_name)
298 .add_attribute("description", collection_info.description)
299 .add_attribute("image", collection_info.image)
300 .check_add_attribute(
301 &collection_info.external_link,
302 "external_link",
303 collection_info
304 .external_link
305 .as_ref()
306 .unwrap_or(&String::from("")),
307 )
308 .add_attribute("is_native", fund_info.is_native.to_string())
309 .add_attribute("denom", fund_info.denom.to_string())
310 .check_add_attribute(
311 &fund_info.cw20_address,
312 "cw20_address",
313 fund_info
314 .cw20_address
315 .as_ref()
316 .unwrap_or(&Addr::unchecked("")),
317 )
318 .check_add_attribute(
319 &collection_config.start_time,
320 "start_time",
321 collection_config
322 .start_time
323 .unwrap_or(Timestamp::from_nanos(0))
324 .to_string(),
325 )
326 .check_add_attribute(
327 &collection_config.max_token_limit,
328 "max_token_limit",
329 collection_config.max_token_limit.unwrap_or(0).to_string(),
330 )
331 .check_add_attribute(
332 &collection_config.per_address_limit,
333 "per_address_limit",
334 collection_config.per_address_limit.unwrap_or(0).to_string(),
335 )
336 .check_add_attribute(
337 &collection_config.ipfs_link,
338 "ipfs_link",
339 collection_config
340 .ipfs_link
341 .as_ref()
342 .unwrap_or(&String::from("")),
343 )
344 .get(),
345 ))
346}
347
348pub fn execute_update_public_collection_creation(
349 deps: DepsMut,
350 env: Env,
351 info: MessageInfo,
352 public_collection_creation: bool,
353) -> Result<Response, ContractError> {
354 let hub_addr = HUB_ADDR.may_load(deps.storage)?;
355 let operators = OPERATORS.may_load(deps.storage)?;
356 let mut config = CONFIG.load(deps.storage)?;
357
358 check_admin_privileges(
359 &info.sender,
360 &env.contract.address,
361 &config.admin,
362 hub_addr,
363 operators,
364 )?;
365
366 config.public_collection_creation = public_collection_creation;
367 CONFIG.save(deps.storage, &config)?;
368
369 Ok(
370 ResponseHelper::new_module("mint", "update_public_collection_creation").add_event(
371 EventHelper::new("mint_update_public_collection_creation")
372 .add_attribute(
373 "public_collection_creation",
374 public_collection_creation.to_string(),
375 )
376 .get(),
377 ),
378 )
379}
380
381pub fn execute_update_collection_mint_lock(
382 deps: DepsMut,
383 env: Env,
384 info: MessageInfo,
385 collection_id: u32,
386 lock: bool,
387) -> Result<Response, ContractError> {
388 let hub_addr = HUB_ADDR.may_load(deps.storage)?;
389 let operators = OPERATORS.may_load(deps.storage)?;
390 let config = CONFIG.load(deps.storage)?;
391
392 check_admin_privileges(
393 &info.sender,
394 &env.contract.address,
395 &config.admin,
396 hub_addr,
397 operators,
398 )?;
399
400 MINT_LOCKS.save(deps.storage, collection_id, &lock)?;
401
402 Ok(
403 ResponseHelper::new_module("mint", "update_mint_lock").add_event(
404 EventHelper::new("mint_update_mint_lock")
405 .add_attribute("collection_id", collection_id.to_string())
406 .add_attribute("mint_lock", lock.to_string())
407 .get(),
408 ),
409 )
410}
411
412fn execute_mint(
413 deps: DepsMut,
414 _env: Env,
415 info: MessageInfo,
416 collection_id: u32,
417 metadata_id: Option<u32>,
418) -> Result<Response, ContractError> {
419 let mint_lock = MINT_LOCKS.load(deps.storage, collection_id)?;
420 if mint_lock {
421 return Err(ContractError::LockedMint {});
422 }
423
424 let mint_msg = MintMsg {
425 collection_id,
426 recipient: info.sender.to_string(),
427 metadata_id,
428 };
429 let msgs: Vec<CosmosMsg> =
430 process_minting_prices(&deps, &info, &info.sender.to_string(), collection_id, None)?;
431
432 _execute_mint(deps, "mint", msgs, mint_msg)
433}
434
435fn execute_admin_mint(
436 deps: DepsMut,
437 env: Env,
438 info: MessageInfo,
439 collection_id: u32,
440 recipient: String,
441 metadata_id: Option<u32>,
442) -> Result<Response, ContractError> {
443 let hub_addr = HUB_ADDR.may_load(deps.storage)?;
444 let operators = OPERATORS.may_load(deps.storage)?;
445 let config = CONFIG.load(deps.storage)?;
446
447 check_admin_privileges(
448 &info.sender,
449 &env.contract.address,
450 &config.admin,
451 hub_addr,
452 operators,
453 )?;
454
455 let recipient = deps.api.addr_validate(&recipient)?;
456
457 let msgs: Vec<CosmosMsg> = vec![];
458 let mint_msg = MintMsg {
459 collection_id,
460 recipient: recipient.to_string(),
461 metadata_id,
462 };
463
464 _execute_mint(deps, "admin_mint", msgs, mint_msg)
465}
466
467fn execute_permission_mint(
468 deps: DepsMut,
469 env: Env,
470 info: MessageInfo,
471 permission_msg: Binary,
472 mint_msg: MintMsg,
473) -> Result<Response, ContractError> {
474 let hub_addr = HUB_ADDR.may_load(deps.storage)?;
475 let operators = OPERATORS.may_load(deps.storage)?;
476 let config = CONFIG.load(deps.storage)?;
477 check_admin_privileges(
478 &info.sender,
479 &env.contract.address,
480 &config.admin,
481 hub_addr.clone(),
482 operators,
483 )?;
484
485 let permission_module_addr = StorageHelper::query_module_address(
486 &deps.querier,
487 &hub_addr.unwrap(),
488 Modules::Permission.to_string(),
489 )?;
490
491 let mut msgs: Vec<WasmMsg> = vec![];
492
493 let permission_msg = PermissionExecuteMsg::Check {
494 module: Modules::Mint.to_string(),
495 msg: permission_msg,
496 };
497 msgs.push(WasmMsg::Execute {
498 contract_addr: permission_module_addr.to_string(),
499 msg: to_binary(&permission_msg)?,
500 funds: info.funds.clone(),
501 });
502
503 msgs.push(WasmMsg::Execute {
504 contract_addr: env.contract.address.to_string(),
505 msg: to_binary(&ExecuteMsg::AdminMint {
506 collection_id: mint_msg.collection_id,
507 recipient: mint_msg.recipient.clone(),
508 metadata_id: mint_msg.metadata_id,
509 })?,
510 funds: info.funds,
511 });
512
513 Ok(ResponseHelper::new_module("mint", "permission_mint")
514 .add_messages(msgs)
515 .add_event(
516 EventHelper::new("mint_permission_mint")
517 .add_attribute("collection_id", mint_msg.collection_id.to_string())
518 .add_attribute("recipient", mint_msg.recipient)
519 .check_add_attribute(
520 &mint_msg.metadata_id,
521 "metadata_id",
522 mint_msg.metadata_id.as_ref().unwrap_or(&0).to_string(),
523 )
524 .get(),
525 ))
526}
527
528fn _execute_mint(
529 deps: DepsMut,
530 action: &str,
531 mut msgs: Vec<CosmosMsg>,
532 mint_msg: MintMsg,
533) -> Result<Response, ContractError> {
534 let collection_addr = COLLECTION_ADDRS.load(deps.storage, mint_msg.collection_id)?;
535
536 let msg = KompleTokenModule(collection_addr)
537 .mint_msg(mint_msg.recipient.clone(), mint_msg.metadata_id)?;
538 msgs.push(msg.into());
539
540 Ok(ResponseHelper::new_module("mint", action)
541 .add_messages(msgs)
542 .add_event(
543 EventHelper::new(format!("mint_{}", action))
544 .add_attribute("recipient", mint_msg.recipient)
545 .add_attribute("collection_id", mint_msg.collection_id.to_string())
546 .check_add_attribute(
547 &mint_msg.metadata_id,
548 "metadata_id",
549 mint_msg.metadata_id.as_ref().unwrap_or(&0).to_string(),
550 )
551 .get(),
552 ))
553}
554
555fn execute_update_linked_collections(
556 deps: DepsMut,
557 env: Env,
558 info: MessageInfo,
559 collection_id: u32,
560 linked_collections: Vec<u32>,
561) -> Result<Response, ContractError> {
562 let hub_addr = HUB_ADDR.may_load(deps.storage)?;
563 let operators = OPERATORS.may_load(deps.storage)?;
564 let config = CONFIG.load(deps.storage)?;
565
566 check_admin_privileges(
567 &info.sender,
568 &env.contract.address,
569 &config.admin,
570 hub_addr,
571 operators,
572 )?;
573
574 if linked_collections.contains(&collection_id) {
575 return Err(ContractError::SelfLinkedCollection {});
576 };
577
578 let mut ids_to_check = vec![collection_id];
579 ids_to_check.extend(&linked_collections);
580 check_collection_ids_exists(&deps, &ids_to_check)?;
581
582 LINKED_COLLECTIONS.save(deps.storage, collection_id, &linked_collections)?;
583
584 Ok(Response::new()
585 .add_attribute("name", "komple_framework")
586 .add_attribute("module", "mint")
587 .add_attribute("action", "update_linked_collections")
588 .add_event(EventHelper::new("mint_update_linked_collections").get()))
589}
590
591fn execute_update_collection_status(
592 deps: DepsMut,
593 env: Env,
594 info: MessageInfo,
595 collection_id: u32,
596 is_blacklist: bool,
597) -> Result<Response, ContractError> {
598 let hub_addr = HUB_ADDR.may_load(deps.storage)?;
599 let operators = OPERATORS.may_load(deps.storage)?;
600 let config = CONFIG.load(deps.storage)?;
601
602 check_admin_privileges(
603 &info.sender,
604 &env.contract.address,
605 &config.admin,
606 hub_addr,
607 operators,
608 )?;
609
610 match is_blacklist {
611 true => {
613 if BLACKLIST_COLLECTION_ADDRS.has(deps.storage, collection_id) {
614 return Err(ContractError::AlreadyBlacklisted {});
615 };
616
617 let collection_addr = COLLECTION_ADDRS.may_load(deps.storage, collection_id)?;
618 if collection_addr.is_none() {
619 return Err(ContractError::CollectionIdNotFound {});
620 };
621
622 COLLECTION_ADDRS.remove(deps.storage, collection_id);
623 BLACKLIST_COLLECTION_ADDRS.save(
624 deps.storage,
625 collection_id,
626 &collection_addr.unwrap(),
627 )?;
628 }
629 false => {
631 if COLLECTION_ADDRS.has(deps.storage, collection_id) {
632 return Err(ContractError::AlreadyWhitelistlisted {});
633 };
634
635 let collection_addr =
636 BLACKLIST_COLLECTION_ADDRS.may_load(deps.storage, collection_id)?;
637 if collection_addr.is_none() {
638 return Err(ContractError::CollectionIdNotFound {});
639 };
640
641 BLACKLIST_COLLECTION_ADDRS.remove(deps.storage, collection_id);
642 COLLECTION_ADDRS.save(deps.storage, collection_id, &collection_addr.unwrap())?;
643 }
644 }
645
646 Ok(Response::new()
647 .add_attribute("name", "komple_framework")
648 .add_attribute("module", "mint")
649 .add_attribute("action", "update_collection_status")
650 .add_event(
651 EventHelper::new("mint_update_collection_status")
652 .add_attribute("collection_id", collection_id.to_string())
653 .add_attribute("is_blacklist", is_blacklist.to_string())
654 .get(),
655 ))
656}
657
658fn execute_update_creators(
659 deps: DepsMut,
660 env: Env,
661 info: MessageInfo,
662 mut addrs: Vec<String>,
663) -> Result<Response, ContractError> {
664 let hub_addr = HUB_ADDR.may_load(deps.storage)?;
665 let operators = OPERATORS.may_load(deps.storage)?;
666 let config = CONFIG.load(deps.storage)?;
667
668 check_admin_privileges(
669 &info.sender,
670 &env.contract.address,
671 &config.admin,
672 hub_addr,
673 operators,
674 )?;
675
676 addrs.sort_unstable();
677 addrs.dedup();
678
679 let mut event_attributes: Vec<Attribute> = vec![];
680
681 let addrs = addrs
682 .iter()
683 .map(|addr| -> StdResult<Addr> {
684 let addr = deps.api.addr_validate(addr)?;
685 event_attributes.push(Attribute {
686 key: "addrs".to_string(),
687 value: addr.to_string(),
688 });
689 Ok(addr)
690 })
691 .collect::<StdResult<Vec<Addr>>>()?;
692
693 CREATORS.save(deps.storage, &addrs)?;
694
695 Ok(
696 ResponseHelper::new_module("mint", "update_creators").add_event(
697 EventHelper::new("mint_update_creators")
698 .add_attributes(event_attributes)
699 .get(),
700 ),
701 )
702}
703
704fn execute_receive(
705 deps: DepsMut,
706 _env: Env,
707 info: MessageInfo,
708 cw20_receive_msg: Cw20ReceiveMsg,
709) -> Result<Response, ContractError> {
710 let msg: ReceiveMsg = from_binary(&cw20_receive_msg.msg)?;
711 let sender = cw20_receive_msg.sender;
712 let amount = cw20_receive_msg.amount;
713 match msg {
714 ReceiveMsg::Mint {
715 collection_id,
716 metadata_id,
717 } => {
718 let mint_lock = MINT_LOCKS.load(deps.storage, collection_id)?;
719 if mint_lock {
720 return Err(ContractError::LockedMint {});
721 }
722
723 let mint_msg = MintMsg {
724 collection_id,
725 recipient: info.sender.to_string(),
726 metadata_id,
727 };
728 let msgs: Vec<CosmosMsg> =
729 process_minting_prices(&deps, &info, &sender, collection_id, Some(amount))?;
730
731 _execute_mint(deps, "mint", msgs, mint_msg)
732 }
733 }
734}
735
736fn process_minting_prices(
737 deps: &DepsMut,
738 info: &MessageInfo,
739 recipient: &str,
740 collection_id: u32,
741 cw20_token_amount: Option<Uint128>,
742) -> Result<Vec<CosmosMsg>, ContractError> {
743 let hub_addr = HUB_ADDR.load(deps.storage)?;
744 let config = CONFIG.load(deps.storage)?;
745
746 let mut msgs: Vec<CosmosMsg> = vec![];
747 let mut total_price = Uint128::zero();
748 let mut is_whitelist = false;
749
750 let fee_module_addr_res =
752 StorageHelper::query_module_address(&deps.querier, &hub_addr, Modules::Fee.to_string());
753 let fee_module_addr = match fee_module_addr_res {
754 Ok(addr) => Some(addr),
755 Err(_) => None,
756 };
757
758 let collection_fund_info = COLLECTION_FUND_INFO.load(deps.storage, collection_id)?;
760
761 let collection_addr = COLLECTION_ADDRS.load(deps.storage, collection_id)?;
763 let sub_modules = StorageHelper::query_token_sub_modules(&deps.querier, &collection_addr)?;
764
765 if let Some(whitelist_addr) = sub_modules.whitelist {
767 let res = KompleWhitelistHelper::new(whitelist_addr.clone()).query_is_active(&deps.querier);
768
769 if let Ok(is_active) = res {
771 if is_active {
772 let query_key = StorageHelper::get_map_storage_key(
774 WHITELIST_NAMESPACE,
775 &[recipient.as_bytes()],
776 )?;
777 let res = StorageHelper::query_storage::<bool>(
778 &deps.querier,
779 &whitelist_addr,
780 &query_key,
781 )?;
782 if res.is_none() {
783 return Err(ContractError::AddressNotWhitelisted {});
784 }
785
786 if fee_module_addr.is_some() {
788 let res = StorageHelper::query_fixed_fee(
790 &deps.querier,
791 &fee_module_addr.as_ref().unwrap(),
792 Modules::Mint.to_string(),
793 MintFees::new_whitelist_price(collection_id),
794 );
795
796 if let Ok(fixed_fee_response) = res {
798 if !fixed_fee_response.value.is_zero() {
800 let msg = match collection_fund_info.is_native {
801 true => CosmosMsg::Bank(BankMsg::Send {
802 to_address: config.admin.to_string(),
803 amount: coins(
804 fixed_fee_response.value.u128(),
805 collection_fund_info.denom.to_string(),
806 ),
807 }),
808 false => CosmosMsg::Wasm(WasmMsg::Execute {
809 contract_addr: collection_fund_info
810 .cw20_address
811 .as_ref()
812 .unwrap()
813 .to_string(),
814 msg: to_binary(&Cw20ExecuteMsg::Transfer {
815 recipient: config.admin.to_string(),
816 amount: fixed_fee_response.value,
817 })?,
818 funds: vec![],
819 }),
820 };
821 msgs.push(msg.into());
822 total_price += fixed_fee_response.value;
823 }
824 }
825 };
826
827 is_whitelist = true;
828 }
829 }
830 }
831
832 if !is_whitelist {
834 if fee_module_addr.is_some() {
836 let res = StorageHelper::query_fixed_fee(
838 &deps.querier,
839 &fee_module_addr.unwrap(),
840 Modules::Mint.to_string(),
841 MintFees::new_price(collection_id),
842 );
843 if let Ok(fixed_fee_response) = res {
844 let msg = match collection_fund_info.is_native {
845 true => CosmosMsg::Bank(BankMsg::Send {
846 to_address: config.admin.to_string(),
847 amount: coins(
848 fixed_fee_response.value.u128(),
849 collection_fund_info.denom.to_string(),
850 ),
851 }),
852 false => CosmosMsg::Wasm(WasmMsg::Execute {
853 contract_addr: collection_fund_info
854 .cw20_address
855 .as_ref()
856 .unwrap()
857 .to_string(),
858 msg: to_binary(&Cw20ExecuteMsg::Transfer {
859 recipient: config.admin.to_string(),
860 amount: fixed_fee_response.value,
861 })?,
862 funds: vec![],
863 }),
864 };
865 msgs.push(msg.into());
866 total_price += fixed_fee_response.value;
867 }
868 }
869 }
870
871 if !total_price.is_zero() {
872 if collection_fund_info.is_native {
873 check_single_coin(
874 &info,
875 Coin {
876 denom: collection_fund_info.denom,
877 amount: total_price,
878 },
879 )?;
880 } else {
881 if cw20_token_amount.is_none() && cw20_token_amount.unwrap() != total_price {
882 return Err(FundsError::InvalidCw20Token {}.into());
883 };
884 }
885 };
886
887 Ok(msgs)
888}
889
890fn check_collection_ids_exists(
891 deps: &DepsMut,
892 collection_ids: &Vec<u32>,
893) -> Result<(), ContractError> {
894 for collection_id in collection_ids {
895 if !COLLECTION_ADDRS.has(deps.storage, *collection_id) {
896 return Err(ContractError::CollectionIdNotFound {});
897 }
898 }
899 Ok(())
900}
901
902#[cfg_attr(not(feature = "library"), entry_point)]
903pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
904 match msg {
905 QueryMsg::Config {} => to_binary(&query_config(deps)?),
906 QueryMsg::CollectionAddress { collection_id } => {
907 to_binary(&query_collection_address(deps, collection_id)?)
908 }
909 QueryMsg::CollectionInfo { collection_id } => {
910 to_binary(&query_collection_info(deps, collection_id)?)
911 }
912 QueryMsg::Operators {} => to_binary(&query_operators(deps)?),
913 QueryMsg::LinkedCollections { collection_id } => {
914 to_binary(&query_linked_collections(deps, collection_id)?)
915 }
916 QueryMsg::Collections {
917 blacklist,
918 start_after,
919 limit,
920 } => to_binary(&query_collections(deps, blacklist, start_after, limit)?),
921 QueryMsg::Creators {} => to_binary(&query_creators(deps)?),
922 QueryMsg::MintLock { collection_id } => to_binary(&query_mint_lock(deps, collection_id)?),
923 }
924}
925
926fn query_config(deps: Deps) -> StdResult<ResponseWrapper<Config>> {
927 let config = CONFIG.load(deps.storage)?;
928 Ok(ResponseWrapper::new("config", config))
929}
930
931fn query_collection_address(deps: Deps, collection_id: u32) -> StdResult<ResponseWrapper<String>> {
932 let addr = COLLECTION_ADDRS.load(deps.storage, collection_id)?;
933 Ok(ResponseWrapper::new("collection_address", addr.to_string()))
934}
935
936fn query_collection_info(
937 deps: Deps,
938 collection_id: u32,
939) -> StdResult<ResponseWrapper<CollectionInfo>> {
940 let collection_info = COLLECTION_INFO.load(deps.storage, collection_id)?;
941 Ok(ResponseWrapper::new("collection_info", collection_info))
942}
943
944fn query_operators(deps: Deps) -> StdResult<ResponseWrapper<Vec<String>>> {
945 let addrs = OPERATORS.may_load(deps.storage)?;
946 let addrs = match addrs {
947 Some(addrs) => addrs.iter().map(|a| a.to_string()).collect(),
948 None => vec![],
949 };
950 Ok(ResponseWrapper::new("operators", addrs))
951}
952
953fn query_linked_collections(
954 deps: Deps,
955 collection_id: u32,
956) -> StdResult<ResponseWrapper<Vec<u32>>> {
957 let linked_collection_ids = LINKED_COLLECTIONS.may_load(deps.storage, collection_id)?;
958 let linked_collection_ids = match linked_collection_ids {
959 Some(linked_collection_ids) => linked_collection_ids,
960 None => vec![],
961 };
962 Ok(ResponseWrapper::new(
963 "linked_collections",
964 linked_collection_ids,
965 ))
966}
967
968fn query_collections(
969 deps: Deps,
970 blacklist: bool,
971 start_after: Option<u32>,
972 limit: Option<u8>,
973) -> StdResult<ResponseWrapper<Vec<CollectionsResponse>>> {
974 let limit = limit.unwrap_or(30) as usize;
975 let start = start_after.map(Bound::exclusive);
976
977 let collections_state = match blacklist {
978 true => BLACKLIST_COLLECTION_ADDRS,
979 false => COLLECTION_ADDRS,
980 };
981
982 let collections = collections_state
983 .range(deps.storage, start, None, Order::Ascending)
984 .take(limit)
985 .map(|item| {
986 let (collection_id, address) = item.unwrap();
987 CollectionsResponse {
988 collection_id,
989 address: address.to_string(),
990 }
991 })
992 .collect::<Vec<CollectionsResponse>>();
993
994 Ok(ResponseWrapper::new("collections", collections))
995}
996
997fn query_creators(deps: Deps) -> StdResult<ResponseWrapper<Vec<String>>> {
998 let addrs = CREATORS.may_load(deps.storage)?;
999 let addrs = match addrs {
1000 Some(addrs) => addrs.iter().map(|a| a.to_string()).collect(),
1001 None => vec![],
1002 };
1003 Ok(ResponseWrapper::new("creators", addrs))
1004}
1005
1006fn query_mint_lock(deps: Deps, collection_id: u32) -> StdResult<ResponseWrapper<bool>> {
1007 let mint_lock = MINT_LOCKS.load(deps.storage, collection_id)?;
1008 Ok(ResponseWrapper::new("mint_lock", mint_lock))
1009}
1010
1011#[cfg_attr(not(feature = "library"), entry_point)]
1012pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
1013 if msg.id != TOKEN_INSTANTIATE_REPLY_ID {
1014 return Err(ContractError::InvalidReplyID {});
1015 }
1016
1017 let reply = parse_reply_instantiate_data(msg);
1018 match reply {
1019 Ok(res) => {
1020 let collection_id = COLLECTION_ID.load(deps.storage)?;
1021 COLLECTION_ADDRS.save(
1022 deps.storage,
1023 collection_id,
1024 &Addr::unchecked(res.contract_address),
1025 )?;
1026 Ok(Response::default().add_attribute("action", "instantiate_token_reply"))
1027 }
1028 Err(_) => Err(ContractError::TokenInstantiateError {}),
1029 }
1030}
1031
1032#[cfg_attr(not(feature = "library"), entry_point)]
1033pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
1034 let version: Version = CONTRACT_VERSION.parse()?;
1035 let contract_version: cw2::ContractVersion = get_contract_version(deps.storage)?;
1036 let storage_version: Version = contract_version.version.parse()?;
1037
1038 if contract_version.contract != CONTRACT_NAME {
1039 return Err(
1040 StdError::generic_err("New version name should match the current version").into(),
1041 );
1042 }
1043 if storage_version >= version {
1044 return Err(
1045 StdError::generic_err("New version cannot be smaller than current version").into(),
1046 );
1047 }
1048
1049 set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
1050
1051 Ok(Response::default())
1052}