1#[cfg(not(feature = "library"))]
2use cosmwasm_std::entry_point;
3use cosmwasm_std::{
4 from_binary, to_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Reply,
5 ReplyOn, Response, StdError, StdResult, SubMsg, Timestamp, WasmMsg,
6};
7use cw2::{get_contract_version, set_contract_version, ContractVersion};
8use cw_utils::parse_reply_instantiate_data;
9use komple_framework_types::modules::metadata::Metadata as MetadataType;
10use komple_framework_types::modules::mint::Collections;
11use komple_framework_types::modules::token::{Locks, SubModules};
12use komple_framework_types::shared::query::ResponseWrapper;
13use komple_framework_types::shared::RegisterMsg;
14use komple_framework_utils::check_admin_privileges;
15use komple_framework_utils::response::{EventHelper, ResponseHelper};
16use komple_framework_utils::shared::execute_update_operators;
17use komple_framework_whitelist_module::helper::KompleWhitelistHelper;
18use semver::Version;
19
20use crate::error::ContractError;
21use crate::msg::{
22 ExecuteMsg as TokenExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg as TokenQueryMsg,
23};
24use crate::state::{
25 CollectionConfig, Config, COLLECTION_TYPE, CONFIG, LOCKS, MINTED_TOKENS_PER_ADDR, OPERATORS,
26 PARENT_ADDR, SUB_MODULES, TOKEN_IDS, TOKEN_LOCKS,
27};
28
29use cw721::ContractInfoResponse;
30use cw721_base::{msg::ExecuteMsg as Cw721ExecuteMsg, MintMsg};
31
32use komple_framework_metadata_module::{
33 helper::KompleMetadataModule, state::MetaInfo as MetadataMetaInfo,
34};
35use komple_framework_whitelist_module::msg::InstantiateMsg as WhitelistInstantiateMsg;
36
37pub type Cw721Contract<'a> =
38 cw721_base::Cw721Contract<'a, Empty, Empty, TokenExecuteMsg, TokenQueryMsg>;
39pub type ExecuteMsg = cw721_base::ExecuteMsg<Empty, TokenExecuteMsg>;
40pub type QueryMsg = cw721_base::QueryMsg<TokenQueryMsg>;
41
42const CONTRACT_NAME: &str = "crates.io:komple-framework-token-module";
44const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
45
46const METADATA_MODULE_INSTANTIATE_REPLY_ID: u64 = 1;
47const WHITELIST_MODULE_INSTANTIATE_REPLY_ID: u64 = 2;
48
49#[cfg_attr(not(feature = "library"), entry_point)]
50pub fn instantiate(
51 deps: DepsMut,
52 env: Env,
53 info: MessageInfo,
54 msg: RegisterMsg,
55) -> Result<Response, ContractError> {
56 set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
57
58 if msg.data.is_none() {
59 return Err(ContractError::InvalidInstantiateMsg {});
60 };
61 let data: InstantiateMsg = from_binary(&msg.data.unwrap())?;
62
63 if data.collection_config.start_time.is_some()
64 && env.block.time >= data.collection_config.start_time.unwrap()
65 {
66 return Err(ContractError::InvalidStartTime {});
67 };
68 if data.collection_config.max_token_limit.is_some()
69 && data.collection_config.max_token_limit.unwrap() <= 0
70 {
71 return Err(ContractError::InvalidMaxTokenLimit {});
72 };
73 if data.collection_config.per_address_limit.is_some()
74 && data.collection_config.per_address_limit.unwrap() <= 0
75 {
76 return Err(ContractError::InvalidPerAddressLimit {});
77 };
78 if data.collection_type == Collections::Standard && data.collection_config.ipfs_link.is_none() {
79 return Err(ContractError::IpfsNotFound {});
80 };
81
82 if (data.collection_type == Collections::Standard
83 && data.metadata_info.instantiate_msg.metadata_type != MetadataType::Standard)
84 || (data.collection_type != Collections::Standard
85 && data.metadata_info.instantiate_msg.metadata_type == MetadataType::Standard)
86 {
87 return Err(ContractError::InvalidCollectionMetadataType {});
88 }
89
90 COLLECTION_TYPE.save(deps.storage, &data.collection_type)?;
91
92 let admin = deps.api.addr_validate(&msg.admin)?;
93 let creator = deps.api.addr_validate(&data.creator)?;
94 let config = Config {
95 admin,
96 creator,
97 start_time: data.collection_config.start_time,
98 max_token_limit: data.collection_config.max_token_limit,
99 per_address_limit: data.collection_config.per_address_limit,
100 ipfs_link: data.collection_config.ipfs_link,
101 };
102 CONFIG.save(deps.storage, &config)?;
103
104 let locks = Locks {
105 burn_lock: false,
106 mint_lock: false,
107 transfer_lock: false,
108 send_lock: false,
109 };
110 LOCKS.save(deps.storage, &locks)?;
111
112 TOKEN_IDS.save(deps.storage, &0)?;
113
114 PARENT_ADDR.save(deps.storage, &info.sender)?;
115
116 let sub_modules = SubModules {
117 whitelist: None,
118 metadata: None,
119 };
120 SUB_MODULES.save(deps.storage, &sub_modules)?;
121
122 let contract_info = ContractInfoResponse {
123 name: data.collection_name.clone(),
124 symbol: data.token_info.symbol.clone(),
125 };
126 Cw721Contract::default()
127 .contract_info
128 .save(deps.storage, &contract_info)?;
129
130 let minter = deps.api.addr_validate(&data.token_info.minter)?;
131 Cw721Contract::default()
132 .minter
133 .save(deps.storage, &minter)?;
134
135 let contract_info = deps.querier.query_wasm_contract_info(info.sender.clone())?;
136 let metadata_register_msg = RegisterMsg {
137 admin: config.admin.to_string(),
138 data: Some(to_binary(&data.metadata_info.instantiate_msg)?),
139 };
140 let sub_msg: SubMsg = SubMsg {
141 msg: WasmMsg::Instantiate {
142 code_id: data.metadata_info.code_id,
143 msg: to_binary(&metadata_register_msg)?,
144 funds: vec![],
145 admin: contract_info.admin,
146 label: String::from("Komple Framework Metadata Module"),
147 }
148 .into(),
149 id: METADATA_MODULE_INSTANTIATE_REPLY_ID,
150 gas_limit: None,
151 reply_on: ReplyOn::Success,
152 };
153
154 Ok(ResponseHelper::new_module("token", "instantiate")
155 .add_submessage(sub_msg)
156 .add_event(
157 EventHelper::new("token_instantiate")
158 .add_attribute("mint_module_addr", info.sender)
159 .add_attribute("creator", config.creator)
160 .add_attribute("minter", minter)
161 .get(),
162 ))
163}
164
165#[cfg_attr(not(feature = "library"), entry_point)]
166pub fn execute(
167 deps: DepsMut,
168 env: Env,
169 info: MessageInfo,
170 msg: ExecuteMsg,
171) -> Result<Response, ContractError> {
172 match msg {
173 ExecuteMsg::Extension { msg } => match msg {
174 TokenExecuteMsg::UpdateLocks { locks } => execute_update_locks(deps, env, info, locks),
175 TokenExecuteMsg::UpdateTokenLocks { token_id, locks } => {
176 execute_update_token_locks(deps, env, info, token_id, locks)
177 }
178 TokenExecuteMsg::Mint { owner, metadata_id } => {
179 execute_mint(deps, env, info, owner, metadata_id)
180 }
181 TokenExecuteMsg::Burn { token_id } => execute_burn(deps, env, info, token_id),
182 TokenExecuteMsg::TransferNft {
183 token_id,
184 recipient,
185 } => execute_transfer(deps, env, info, token_id, recipient),
186 TokenExecuteMsg::SendNft {
187 token_id,
188 contract,
189 msg,
190 } => execute_send(deps, env, info, token_id, contract, msg),
191 TokenExecuteMsg::UpdateCollectionConfig { collection_config } => {
192 execute_update_collection_config(deps, env, info, collection_config)
193 }
194 TokenExecuteMsg::AdminTransferNft {
195 recipient,
196 token_id,
197 } => execute_admin_transfer(deps, env, info, token_id, recipient),
198 TokenExecuteMsg::InitWhitelistContract {
199 code_id,
200 instantiate_msg,
201 } => execute_init_whitelist_module(deps, env, info, code_id, instantiate_msg),
202 TokenExecuteMsg::UpdateModuleOperators { addrs } => {
203 let config = CONFIG.load(deps.storage)?;
204 let res = execute_update_operators(
205 deps,
206 info,
207 "token",
208 &env.contract.address,
209 &config.admin,
210 OPERATORS,
211 addrs,
212 );
213 match res {
214 Ok(res) => Ok(res),
215 Err(e) => Err(e.into()),
216 }
217 }
218 },
219 _ => {
220 match msg {
221 Cw721ExecuteMsg::Mint(_mint_msg) => Err(ContractError::Unauthorized {}),
223 Cw721ExecuteMsg::Burn { token_id } => execute_burn(deps, env, info, token_id),
224 Cw721ExecuteMsg::SendNft {
225 token_id,
226 contract,
227 msg,
228 } => execute_send(deps, env, info, token_id, contract, msg),
229 Cw721ExecuteMsg::TransferNft {
230 token_id,
231 recipient,
232 } => execute_transfer(deps, env, info, token_id, recipient),
233 _ => {
234 let res = Cw721Contract::default().execute(deps, env, info, msg);
235 match res {
236 Ok(res) => Ok(res),
237 Err(e) => Err(e.into()),
238 }
239 }
240 }
241 }
242 }
243}
244
245pub fn execute_update_locks(
246 deps: DepsMut,
247 env: Env,
248 info: MessageInfo,
249 locks: Locks,
250) -> Result<Response, ContractError> {
251 let mint_module_addr = PARENT_ADDR.may_load(deps.storage)?;
252 let operators = OPERATORS.may_load(deps.storage)?;
253 let config = CONFIG.load(deps.storage)?;
254
255 check_admin_privileges(
256 &info.sender,
257 &env.contract.address,
258 &config.admin,
259 mint_module_addr,
260 operators,
261 )?;
262
263 LOCKS.save(deps.storage, &locks)?;
264
265 Ok(
266 ResponseHelper::new_module("token", "update_locks").add_event(
267 EventHelper::new("token_update_locks")
268 .add_attribute("action", "update_locks")
269 .add_attribute("mint_lock", locks.mint_lock.to_string())
270 .add_attribute("burn_lock", locks.burn_lock.to_string())
271 .add_attribute("transfer_lock", locks.transfer_lock.to_string())
272 .add_attribute("send_lock", locks.send_lock.to_string())
273 .get(),
274 ),
275 )
276}
277
278pub fn execute_update_token_locks(
279 deps: DepsMut,
280 env: Env,
281 info: MessageInfo,
282 token_id: String,
283 locks: Locks,
284) -> Result<Response, ContractError> {
285 let mint_module_addr = PARENT_ADDR.may_load(deps.storage)?;
286 let operators = OPERATORS.may_load(deps.storage)?;
287 let config = CONFIG.load(deps.storage)?;
288
289 check_admin_privileges(
290 &info.sender,
291 &env.contract.address,
292 &config.admin,
293 mint_module_addr,
294 operators,
295 )?;
296
297 if !Cw721Contract::default().tokens.has(deps.storage, &token_id) {
298 return Err(ContractError::TokenNotFound {});
299 }
300
301 TOKEN_LOCKS.save(deps.storage, &token_id, &locks)?;
302
303 Ok(
304 ResponseHelper::new_module("token", "update_token_locks").add_event(
305 EventHelper::new("token_update_token_locks")
306 .add_attribute("action", "update_token_locks")
307 .add_attribute("token_id", token_id)
308 .add_attribute("mint_lock", locks.mint_lock.to_string())
309 .add_attribute("burn_lock", locks.burn_lock.to_string())
310 .add_attribute("transfer_lock", locks.transfer_lock.to_string())
311 .add_attribute("send_lock", locks.send_lock.to_string())
312 .get(),
313 ),
314 )
315}
316
317pub fn execute_mint(
318 deps: DepsMut,
319 env: Env,
320 info: MessageInfo,
321 owner: String,
322 metadata_id: Option<u32>,
323) -> Result<Response, ContractError> {
324 let config = CONFIG.load(deps.storage)?;
325 let collection_type = COLLECTION_TYPE.load(deps.storage)?;
326
327 let total_minted = MINTED_TOKENS_PER_ADDR
328 .may_load(deps.storage, &owner)?
329 .unwrap_or(0);
330 let token_id = (TOKEN_IDS.load(deps.storage)?) + 1;
331
332 let locks = LOCKS.load(deps.storage)?;
333 if locks.mint_lock {
334 return Err(ContractError::MintLocked {});
335 }
336
337 let token_lock = TOKEN_LOCKS.may_load(deps.storage, &token_id.to_string())?;
338 if token_lock.is_some() && token_lock.unwrap().mint_lock {
339 return Err(ContractError::MintLocked {});
340 }
341
342 if config.max_token_limit.is_some() && token_id > config.max_token_limit.unwrap() {
343 return Err(ContractError::TokenLimitReached {});
344 }
345
346 if config.per_address_limit.is_some() && total_minted + 1 > config.per_address_limit.unwrap() {
347 return Err(ContractError::TokenLimitReached {});
348 }
349
350 if config.start_time.is_some() && env.block.time < config.start_time.unwrap() {
351 return Err(ContractError::MintingNotStarted {});
352 }
353
354 let sub_modules = SUB_MODULES.load(deps.storage)?;
356 if let Some(whitelist_addr) = sub_modules.whitelist {
357 let whitelist_config =
358 KompleWhitelistHelper::new(whitelist_addr).query_config(&deps.querier)?;
359
360 if total_minted + 1 > (whitelist_config.per_address_limit as u32) {
361 return Err(ContractError::TokenLimitReached {});
362 }
363 };
364
365 let mint_msg = MintMsg {
366 token_id: token_id.to_string(),
367 owner: owner.clone(),
368 token_uri: None,
369 extension: Empty {},
370 };
371
372 MINTED_TOKENS_PER_ADDR.save(deps.storage, &owner, &(total_minted + 1))?;
373 TOKEN_IDS.save(deps.storage, &token_id)?;
374
375 let sub_modules = SUB_MODULES.load(deps.storage)?;
376 if sub_modules.metadata.is_none() {
377 return Err(ContractError::MetadataContractNotFound {});
378 };
379
380 let res = Cw721Contract::default().mint(deps, env, info, mint_msg);
381
382 let mut msgs: Vec<CosmosMsg> = vec![];
383
384 if collection_type == Collections::Standard {
387 if config.ipfs_link.is_none() {
388 return Err(ContractError::IpfsNotFound {});
389 };
390
391 let ifps_link = format!("{}/{}", config.ipfs_link.unwrap(), token_id);
392
393 let msg = KompleMetadataModule(sub_modules.metadata.clone().unwrap()).add_metadata_msg(
394 MetadataMetaInfo {
395 image: Some(ifps_link),
396 external_url: None,
397 description: None,
398 youtube_url: None,
399 animation_url: None,
400 },
401 vec![],
402 )?;
403 msgs.push(msg.into())
404 }
405 let msg = KompleMetadataModule(sub_modules.metadata.unwrap())
407 .link_metadata_msg(token_id, metadata_id)?;
408 msgs.push(msg.into());
409
410 match res {
411 Ok(res) => Ok(res
412 .add_messages(msgs)
413 .add_attribute("name", "komple_framework")
414 .add_attribute("module", "token")
415 .add_attribute("action", "mint")
416 .add_event(
417 EventHelper::new("token_mint")
418 .add_attribute("token_id", token_id.to_string())
419 .add_attribute("owner", owner)
420 .check_add_attribute(
421 &metadata_id,
422 "metadata_id",
423 metadata_id.unwrap_or(0).to_string(),
424 )
425 .get(),
426 )),
427 Err(e) => Err(e.into()),
428 }
429}
430
431pub fn execute_burn(
432 deps: DepsMut,
433 env: Env,
434 info: MessageInfo,
435 token_id: String,
436) -> Result<Response, ContractError> {
437 let locks = LOCKS.load(deps.storage)?;
438 if locks.burn_lock {
439 return Err(ContractError::BurnLocked {});
440 }
441
442 let token_lock = TOKEN_LOCKS.may_load(deps.storage, &token_id)?;
443 if token_lock.is_some() && token_lock.unwrap().burn_lock {
444 return Err(ContractError::BurnLocked {});
445 }
446
447 let sub_modules = SUB_MODULES.load(deps.storage)?;
448 if sub_modules.metadata.is_none() {
449 return Err(ContractError::MetadataContractNotFound {});
450 };
451
452 let unlink_metadata_msg = KompleMetadataModule(sub_modules.metadata.unwrap())
453 .unlink_metadata_msg(token_id.parse::<u32>().unwrap())?;
454
455 let res = Cw721Contract::default().execute(
456 deps,
457 env,
458 info,
459 ExecuteMsg::Burn {
460 token_id: token_id.clone(),
461 },
462 );
463 match res {
464 Ok(res) => Ok(res
465 .add_message(unlink_metadata_msg)
466 .add_attribute("name", "komple_framework")
467 .add_attribute("module", "token")
468 .add_attribute("action", "burn")
469 .add_event(
470 EventHelper::new("token_burn")
471 .add_attribute("token_id", token_id)
472 .get(),
473 )),
474 Err(e) => Err(e.into()),
475 }
476}
477
478pub fn execute_transfer(
479 deps: DepsMut,
480 env: Env,
481 info: MessageInfo,
482 token_id: String,
483 recipient: String,
484) -> Result<Response, ContractError> {
485 let locks = LOCKS.load(deps.storage)?;
486 if locks.transfer_lock {
487 return Err(ContractError::TransferLocked {});
488 }
489
490 let token_lock = TOKEN_LOCKS.may_load(deps.storage, &token_id)?;
491 if token_lock.is_some() && token_lock.unwrap().transfer_lock {
492 return Err(ContractError::TransferLocked {});
493 }
494
495 let res = Cw721Contract::default().execute(
496 deps,
497 env,
498 info,
499 ExecuteMsg::TransferNft {
500 recipient: recipient.clone(),
501 token_id: token_id.clone(),
502 },
503 );
504 match res {
505 Ok(res) => Ok(res
506 .add_attribute("name", "komple_framework")
507 .add_attribute("module", "token")
508 .add_attribute("action", "transfer")
509 .add_event(
510 EventHelper::new("token_transfer")
511 .add_attribute("token_id", token_id)
512 .add_attribute("recipient", recipient)
513 .get(),
514 )),
515 Err(e) => Err(e.into()),
516 }
517}
518
519pub fn execute_admin_transfer(
520 deps: DepsMut,
521 env: Env,
522 info: MessageInfo,
523 token_id: String,
524 recipient: String,
525) -> Result<Response, ContractError> {
526 let config = CONFIG.load(deps.storage)?;
527 let mint_module_addr = PARENT_ADDR.may_load(deps.storage)?;
528 let operators = OPERATORS.may_load(deps.storage)?;
529
530 check_admin_privileges(
531 &info.sender,
532 &env.contract.address,
533 &config.admin,
534 mint_module_addr,
535 operators,
536 )?;
537
538 let res = Cw721Contract::default().execute(
539 deps,
540 env,
541 info,
542 ExecuteMsg::TransferNft {
543 recipient: recipient.clone(),
544 token_id: token_id.clone(),
545 },
546 );
547 match res {
548 Ok(res) => Ok(res
549 .add_attribute("name", "komple_framework")
550 .add_attribute("module", "token")
551 .add_attribute("action", "admin_transfer")
552 .add_event(
553 EventHelper::new("token_admin_transfer")
554 .add_attribute("token_id", token_id)
555 .add_attribute("recipient", recipient)
556 .get(),
557 )),
558 Err(e) => Err(e.into()),
559 }
560}
561
562pub fn execute_send(
563 deps: DepsMut,
564 env: Env,
565 info: MessageInfo,
566 token_id: String,
567 contract: String,
568 msg: Binary,
569) -> Result<Response, ContractError> {
570 let locks = LOCKS.load(deps.storage)?;
571 if locks.send_lock {
572 return Err(ContractError::SendLocked {});
573 }
574
575 let token_lock = TOKEN_LOCKS.may_load(deps.storage, &token_id)?;
576 if token_lock.is_some() && token_lock.unwrap().send_lock {
577 return Err(ContractError::SendLocked {});
578 }
579
580 let res = Cw721Contract::default().execute(
581 deps,
582 env,
583 info,
584 ExecuteMsg::SendNft {
585 contract: contract.clone(),
586 token_id: contract.clone(),
587 msg,
588 },
589 );
590 match res {
591 Ok(res) => Ok(res
592 .add_attribute("name", "komple_framework")
593 .add_attribute("module", "token")
594 .add_attribute("action", "send")
595 .add_event(
596 EventHelper::new("token_send")
597 .add_attribute("token_id", token_id)
598 .add_attribute("contract", contract)
599 .get(),
600 )),
601 Err(e) => Err(e.into()),
602 }
603}
604
605fn execute_update_collection_config(
606 deps: DepsMut,
607 env: Env,
608 info: MessageInfo,
609 collection_config: CollectionConfig,
610) -> Result<Response, ContractError> {
611 let mint_module_addr = PARENT_ADDR.may_load(deps.storage)?;
612 let operators = OPERATORS.may_load(deps.storage)?;
613 let mut config = CONFIG.load(deps.storage)?;
614
615 check_admin_privileges(
616 &info.sender,
617 &env.contract.address,
618 &config.admin,
619 mint_module_addr,
620 operators,
621 )?;
622
623 if config.start_time.is_some() && env.block.time >= config.start_time.unwrap() {
625 return Err(ContractError::AlreadyStarted {});
626 };
627 match collection_config.start_time {
628 Some(time) => {
629 if env.block.time >= time {
630 return Err(ContractError::InvalidStartTime {});
631 }
632 config.start_time = collection_config.start_time;
633 }
634 None => config.start_time = None,
635 };
636
637 if collection_config.per_address_limit.is_some()
639 && collection_config.per_address_limit.unwrap() <= 0
640 {
641 return Err(ContractError::InvalidPerAddressLimit {});
642 };
643 config.per_address_limit = collection_config.per_address_limit;
644
645 if collection_config.max_token_limit.is_some()
647 && collection_config.max_token_limit.unwrap() <= 0
648 {
649 return Err(ContractError::InvalidMaxTokenLimit {});
650 };
651 config.max_token_limit = collection_config.max_token_limit;
652
653 if collection_config.ipfs_link.is_some() {
655 config.ipfs_link = collection_config.ipfs_link;
656 };
657
658 CONFIG.save(deps.storage, &config)?;
659
660 Ok(Response::new()
661 .add_attribute("name", "komple_framework")
662 .add_attribute("module", "token")
663 .add_attribute("action", "update_collection_config")
664 .add_event(
665 EventHelper::new("token_update_collection_config")
666 .check_add_attribute(
667 &config.start_time,
668 "start_time",
669 config
670 .start_time
671 .unwrap_or(Timestamp::from_nanos(0))
672 .to_string(),
673 )
674 .check_add_attribute(
675 &config.max_token_limit,
676 "max_token_limit",
677 config.max_token_limit.unwrap_or(0).to_string(),
678 )
679 .check_add_attribute(
680 &config.per_address_limit,
681 "per_address_limit",
682 config.per_address_limit.unwrap_or(0).to_string(),
683 )
684 .check_add_attribute(
685 &config.ipfs_link,
686 "ipfs_link",
687 config.ipfs_link.as_ref().unwrap_or(&String::from("")),
688 )
689 .get(),
690 ))
691}
692
693fn execute_init_whitelist_module(
694 deps: DepsMut,
695 env: Env,
696 info: MessageInfo,
697 code_id: u64,
698 instantiate_msg: WhitelistInstantiateMsg,
699) -> Result<Response, ContractError> {
700 let mint_module_addr = PARENT_ADDR.load(deps.storage)?;
701 let operators = OPERATORS.may_load(deps.storage)?;
702 let config = CONFIG.load(deps.storage)?;
703
704 check_admin_privileges(
705 &info.sender,
706 &env.contract.address,
707 &config.admin,
708 Some(mint_module_addr.clone()),
709 operators,
710 )?;
711
712 let contract_info = deps
713 .querier
714 .query_wasm_contract_info(env.contract.address)?;
715 let register_msg = RegisterMsg {
716 admin: config.admin.to_string(),
717 data: Some(to_binary(&instantiate_msg)?),
718 };
719 let sub_msg: SubMsg = SubMsg {
720 msg: WasmMsg::Instantiate {
721 code_id,
722 msg: to_binary(®ister_msg)?,
723 funds: vec![],
724 admin: contract_info.admin,
725 label: String::from("Komple Framework Whitelist Module"),
726 }
727 .into(),
728 id: WHITELIST_MODULE_INSTANTIATE_REPLY_ID,
729 gas_limit: None,
730 reply_on: ReplyOn::Success,
731 };
732
733 Ok(ResponseHelper::new_module("token", "init_whitelist_module")
734 .add_submessage(sub_msg)
735 .add_event(EventHelper::new("token_init_whitelist_module").get()))
736}
737
738#[cfg_attr(not(feature = "library"), entry_point)]
739pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
740 match msg {
741 QueryMsg::Extension { msg } => match msg {
742 TokenQueryMsg::Config {} => to_binary(&query_config(deps)?),
743 TokenQueryMsg::Locks {} => to_binary(&query_locks(deps)?),
744 TokenQueryMsg::TokenLocks { token_id } => {
745 to_binary(&query_token_locks(deps, token_id)?)
746 }
747 TokenQueryMsg::MintedTokensPerAddress { address } => {
748 to_binary(&query_minted_tokens_per_address(deps, address)?)
749 }
750 TokenQueryMsg::SubModules {} => to_binary(&query_sub_modules(deps)?),
751 TokenQueryMsg::ModuleOperators {} => to_binary(&query_module_operators(deps)?),
752 },
753 _ => Cw721Contract::default().query(deps, env, msg),
754 }
755}
756
757fn query_config(deps: Deps) -> StdResult<ResponseWrapper<Config>> {
758 let config = CONFIG.load(deps.storage)?;
759 Ok(ResponseWrapper::new("locks", config))
760}
761
762fn query_locks(deps: Deps) -> StdResult<ResponseWrapper<Locks>> {
763 let locks = LOCKS.load(deps.storage)?;
764 Ok(ResponseWrapper::new("locks", locks))
765}
766
767fn query_token_locks(deps: Deps, token_id: String) -> StdResult<ResponseWrapper<Locks>> {
768 let locks = TOKEN_LOCKS.load(deps.storage, &token_id)?;
769 Ok(ResponseWrapper::new("locks", locks))
770}
771
772fn query_minted_tokens_per_address(deps: Deps, address: String) -> StdResult<ResponseWrapper<u32>> {
773 let amount = MINTED_TOKENS_PER_ADDR
774 .may_load(deps.storage, &address)?
775 .unwrap_or(0);
776 Ok(ResponseWrapper::new("minted_tokens_per_address", amount))
777}
778
779fn query_sub_modules(deps: Deps) -> StdResult<ResponseWrapper<SubModules>> {
780 let sub_modules = SUB_MODULES.load(deps.storage)?;
781 Ok(ResponseWrapper::new("sub_modules", sub_modules))
782}
783
784fn query_module_operators(deps: Deps) -> StdResult<ResponseWrapper<Vec<String>>> {
785 let operators = OPERATORS.load(deps.storage).unwrap_or_default();
786 Ok(ResponseWrapper::new(
787 "module_operators",
788 operators.iter().map(|o| o.to_string()).collect(),
789 ))
790}
791
792#[cfg_attr(not(feature = "library"), entry_point)]
793pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
794 if msg.id != METADATA_MODULE_INSTANTIATE_REPLY_ID
795 && msg.id != WHITELIST_MODULE_INSTANTIATE_REPLY_ID
796 {
797 return Err(ContractError::InvalidReplyID {});
798 }
799
800 let reply = parse_reply_instantiate_data(msg.clone());
801 match reply {
802 Ok(res) => {
803 let mut sub_modules = SUB_MODULES.load(deps.storage)?;
804 let sub_module: &str;
805 match msg.id {
806 METADATA_MODULE_INSTANTIATE_REPLY_ID => {
807 sub_modules.metadata = Some(Addr::unchecked(res.contract_address));
808 sub_module = "metadata";
809 }
810 WHITELIST_MODULE_INSTANTIATE_REPLY_ID => {
811 sub_modules.whitelist = Some(Addr::unchecked(res.contract_address));
812 sub_module = "whitelist";
813 }
814 _ => unreachable!(),
815 }
816 SUB_MODULES.save(deps.storage, &sub_modules)?;
817 Ok(Response::default()
818 .add_attribute("action", format!("instantiate_{}_reply", sub_module)))
819 }
820 Err(_) => Err(ContractError::ContractsInstantiateError {}),
821 }
822}
823
824#[cfg_attr(not(feature = "library"), entry_point)]
825pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
826 let version: Version = CONTRACT_VERSION.parse()?;
827 let contract_version: ContractVersion = get_contract_version(deps.storage)?;
828 let storage_version: Version = contract_version.version.parse()?;
829
830 if contract_version.contract != CONTRACT_NAME {
831 return Err(
832 StdError::generic_err("New version name should match the current version").into(),
833 );
834 }
835 if storage_version >= version {
836 return Err(
837 StdError::generic_err("New version cannot be smaller than current version").into(),
838 );
839 }
840
841 set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
842
843 Ok(Response::default())
844}