1#![allow(clippy::too_many_arguments)]
2use odra::named_keys::single_value_storage;
3
4use super::{
5 constants::{PREFIX_PAGE_DICTIONARY, TRANSFER_FILTER_CONTRACT},
6 data::CollectionData,
7 error::CEP78Error,
8 events::{
9 Approval, ApprovalForAll, ApprovalRevoked, Burn, MetadataUpdated, Mint, RevokedForAll,
10 Transfer, VariablesSet
11 },
12 metadata::Metadata,
13 modalities::{
14 BurnMode, EventsMode, MetadataMutability, MintingMode, NFTHolderMode, NFTIdentifierMode,
15 NFTKind, NFTMetadataKind, OwnerReverseLookupMode, OwnershipMode, TokenIdentifier,
16 TransferFilterContractResult, WhitelistMode
17 },
18 reverse_lookup::{ReverseLookup, PAGE_SIZE},
19 settings::Settings,
20 utils,
21 whitelist::ACLWhitelist
22};
23use odra::{
24 args::Maybe, casper_event_standard::EventInstance, casper_types::bytesrepr::ToBytes,
25 prelude::*, ContractRef
26};
27
28single_value_storage!(
29 Cep78TransferFilterContract,
30 Address,
31 TRANSFER_FILTER_CONTRACT
32);
33
34#[odra::module(
52 version = "1.5.1",
53 events = [Approval, ApprovalForAll, ApprovalRevoked, Burn, MetadataUpdated, Mint, RevokedForAll, Transfer, VariablesSet],
54 errors = CEP78Error
55)]
56pub struct Cep78 {
57 data: SubModule<CollectionData>,
58 metadata: SubModule<Metadata>,
59 settings: SubModule<Settings>,
60 whitelist: SubModule<ACLWhitelist>,
61 reverse_lookup: SubModule<ReverseLookup>,
62 transfer_filter_contract: SubModule<Cep78TransferFilterContract>
63}
64
65#[odra::module]
66impl Cep78 {
67 pub fn init(
69 &mut self,
70 collection_name: String,
71 collection_symbol: String,
72 total_token_supply: u64,
73 ownership_mode: OwnershipMode,
74 nft_kind: NFTKind,
75 identifier_mode: NFTIdentifierMode,
76 nft_metadata_kind: NFTMetadataKind,
77 metadata_mutability: MetadataMutability,
78 receipt_name: String,
79 allow_minting: Maybe<bool>,
80 minting_mode: Maybe<MintingMode>,
81 holder_mode: Maybe<NFTHolderMode>,
82 whitelist_mode: Maybe<WhitelistMode>,
83 acl_whitelist: Maybe<Vec<Address>>,
84 json_schema: Maybe<String>,
85 burn_mode: Maybe<BurnMode>,
86 operator_burn_mode: Maybe<bool>,
87 owner_reverse_lookup_mode: Maybe<OwnerReverseLookupMode>,
88 events_mode: Maybe<EventsMode>,
89 transfer_filter_contract_contract: Maybe<Address>,
90 additional_required_metadata: Maybe<Vec<NFTMetadataKind>>,
91 optional_metadata: Maybe<Vec<NFTMetadataKind>>
92 ) {
93 let installer = self.caller();
94 let minting_mode = minting_mode.unwrap_or_default();
95 let owner_reverse_lookup_mode = owner_reverse_lookup_mode.unwrap_or_default();
96 let acl_white_list = acl_whitelist.unwrap_or_default();
97 let whitelist_mode = whitelist_mode.unwrap_or_default();
98 let json_schema = json_schema.unwrap_or_default();
99 let is_whitelist_empty = acl_white_list.is_empty();
100
101 if MintingMode::Acl != minting_mode && !is_whitelist_empty {
103 self.revert(CEP78Error::InvalidMintingMode)
104 }
105
106 if MintingMode::Acl == minting_mode
108 && is_whitelist_empty
109 && WhitelistMode::Locked == whitelist_mode
110 {
111 self.revert(CEP78Error::EmptyACLWhitelist)
112 }
113
114 if ownership_mode == OwnershipMode::Minter
123 && minting_mode == MintingMode::Installer
124 && owner_reverse_lookup_mode == OwnerReverseLookupMode::Complete
125 {
126 self.revert(CEP78Error::InvalidReportingMode)
127 }
128
129 if nft_metadata_kind == NFTMetadataKind::CustomValidated && json_schema.is_empty() {
131 self.revert(CEP78Error::MissingJsonSchema)
132 }
133
134 if OwnerReverseLookupMode::TransfersOnly == owner_reverse_lookup_mode
136 && OwnershipMode::Transferable != ownership_mode
137 {
138 self.revert(CEP78Error::OwnerReverseLookupModeNotTransferable)
139 }
140
141 if ownership_mode != OwnershipMode::Transferable
142 && transfer_filter_contract_contract.is_some()
143 {
144 self.revert(CEP78Error::TransferFilterContractNeedsTransferableMode)
145 }
146
147 self.data.init(
148 collection_name,
149 collection_symbol,
150 total_token_supply,
151 installer
152 );
153 self.settings.init(
154 allow_minting.unwrap_or(true),
155 minting_mode,
156 ownership_mode,
157 nft_kind,
158 holder_mode.unwrap_or_default(),
159 burn_mode.unwrap_or_default(),
160 events_mode.unwrap_or_default(),
161 operator_burn_mode.unwrap_or_default()
162 );
163
164 self.reverse_lookup
165 .init(owner_reverse_lookup_mode, receipt_name, total_token_supply);
166
167 self.whitelist.init(acl_white_list.clone(), whitelist_mode);
168
169 self.metadata.init(
170 nft_metadata_kind,
171 additional_required_metadata,
172 optional_metadata,
173 metadata_mutability,
174 identifier_mode,
175 json_schema
176 );
177
178 if let Maybe::Some(transfer_filter_contract_contract) = transfer_filter_contract_contract {
179 self.transfer_filter_contract
180 .set(transfer_filter_contract_contract);
181 }
182 }
183
184 pub fn set_variables(
189 &mut self,
190 allow_minting: Maybe<bool>,
191 acl_whitelist: Maybe<Vec<Address>>,
192 operator_burn_mode: Maybe<bool>
193 ) {
194 let installer = self.data.installer();
195 self.ensure_caller(installer);
196
197 if let Maybe::Some(allow_minting) = allow_minting {
198 self.settings.set_allow_minting(allow_minting);
199 }
200
201 if let Maybe::Some(operator_burn_mode) = operator_burn_mode {
202 self.settings.set_operator_burn_mode(operator_burn_mode);
203 }
204
205 self.whitelist.update(acl_whitelist);
206 self.emit_ces_event(VariablesSet::new());
207 }
208
209 pub fn mint(
220 &mut self,
221 token_owner: Address,
222 token_meta_data: String,
223 token_hash: Maybe<String>
224 ) {
225 if !self.settings.allow_minting() {
226 self.revert(CEP78Error::MintingIsPaused);
227 }
228
229 let total_token_supply = self.data.total_token_supply();
230 let minted_tokens_count = self.data.number_of_minted_tokens();
231
232 if minted_tokens_count >= total_token_supply {
233 self.revert(CEP78Error::TokenSupplyDepleted);
234 }
235
236 let minting_mode = self.settings.minting_mode();
237 let caller = self.verified_caller();
238
239 if MintingMode::Installer == minting_mode {
240 match caller {
241 Address::Account(_) => {
242 let installer_account = self.data.installer();
243 if caller != installer_account {
244 self.revert(CEP78Error::InvalidMinter)
245 }
246 }
247 _ => self.revert(CEP78Error::InvalidKey)
248 }
249 }
250
251 if MintingMode::Acl == minting_mode && !self.whitelist.is_whitelisted(&caller) {
252 match caller {
253 Address::Contract(_) => self.revert(CEP78Error::UnlistedContractHash),
254 Address::Account(_) => self.revert(CEP78Error::InvalidMinter)
255 }
256 }
257
258 let identifier_mode = self.metadata.get_identifier_mode();
259 let optional_token_hash: String = token_hash.unwrap_or_default();
260 let token_identifier: TokenIdentifier = match identifier_mode {
261 NFTIdentifierMode::Ordinal => TokenIdentifier::Index(minted_tokens_count),
262 NFTIdentifierMode::Hash => TokenIdentifier::Hash(if optional_token_hash.is_empty() {
263 let hash = self.__env.hash(token_meta_data.clone());
264 base16::encode_lower(&hash)
265 } else {
266 optional_token_hash
267 })
268 };
269 let token_id = token_identifier.to_string();
270
271 if self.token_exists_by_hash(&token_id) {
273 self.revert(CEP78Error::DuplicateIdentifier)
274 }
275
276 self.metadata.update_or_revert(&token_meta_data, &token_id);
277
278 let token_owner = if self.is_transferable_or_assigned() {
279 token_owner
280 } else {
281 caller
282 };
283
284 self.data.set_owner(&token_id, token_owner);
285 self.data.set_issuer(&token_id, caller);
286 self.data.mark_not_burnt(&token_id);
287
288 self.data.increment_counter(&token_owner);
289 self.data.increment_number_of_minted_tokens();
290
291 self.emit_ces_event(Mint::new(token_owner, token_id.clone(), token_meta_data));
292
293 self.reverse_lookup.on_mint(&token_owner, &token_identifier)
294 }
295
296 pub fn burn(&mut self, token_id: Maybe<u64>, token_hash: Maybe<String>) {
303 let token_identifier = self.token_identifier(token_id, token_hash);
304 let token_id = token_identifier.to_string();
305
306 let token_owner = self.owner_of_by_id(&token_id);
307 let caller = self.__env.caller();
308
309 let is_owner = token_owner == caller;
310 let is_operator = if !is_owner {
311 self.data.operator(token_owner, caller)
312 } else {
313 false
314 };
315
316 if !is_owner && !is_operator {
317 self.revert(CEP78Error::InvalidTokenOwner)
318 };
319
320 self.ensure_burnable();
323 self.ensure_not_burned(&token_id);
324 self.data.mark_burnt(&token_id);
325 self.data.decrement_counter(&token_owner);
326 self.data.decrement_number_of_minted_tokens();
327 self.emit_ces_event(Burn::new(token_owner, token_id, caller));
328
329 self.reverse_lookup.on_burn(&token_owner, &token_identifier);
330 }
331
332 pub fn transfer(
337 &mut self,
338 token_id: Maybe<u64>,
339 token_hash: Maybe<String>,
340 source_key: Address,
341 target_key: Address
342 ) {
343 self.ensure_not_minter_or_assigned();
344
345 let token_identifier = self.checked_token_identifier(token_id, token_hash);
346 let token_id = token_identifier.to_string();
347 self.ensure_not_burned(&token_id);
348 self.ensure_owner(&token_id, &source_key);
349
350 let caller = self.caller();
351 let owner = self.owner_of_by_id(&token_id);
352 let is_owner = owner == caller;
353
354 let is_approved = !is_owner
355 && match self.data.approved(&token_id) {
356 Some(maybe_approved) => caller == maybe_approved,
357 _ => false
358 };
359
360 let is_operator = if !is_owner && !is_approved {
361 self.data.operator(source_key, caller)
362 } else {
363 false
364 };
365
366 if let Some(filter_contract) = self.transfer_filter_contract.get() {
367 let result = TransferFilterContractContractRef::new(self.env(), filter_contract)
368 .can_transfer(source_key, target_key, token_identifier.clone());
369
370 if TransferFilterContractResult::DenyTransfer == result {
371 self.revert(CEP78Error::TransferFilterContractDenied);
372 }
373 }
374
375 if !is_owner && !is_approved && !is_operator {
376 self.revert(CEP78Error::InvalidTokenOwner);
377 }
378
379 match self.data.owner_of(&token_id) {
380 Some(token_actual_owner) => {
381 if token_actual_owner != source_key {
382 self.revert(CEP78Error::InvalidTokenOwner)
383 }
384 }
385 None => self.revert(CEP78Error::MissingOwnerTokenIdentifierKey)
386 }
387
388 let spender = if caller == owner { None } else { Some(caller) };
389 self.transfer_unchecked(token_id, source_key, spender, target_key);
390
391 self.reverse_lookup
392 .on_transfer(&token_identifier, &source_key, &target_key)
393 }
394
395 pub fn approve(&mut self, spender: Address, token_id: Maybe<u64>, token_hash: Maybe<String>) {
399 self.ensure_not_minter_or_assigned();
400
401 let caller = self.caller();
402 let token_identifier = self.checked_token_identifier(token_id, token_hash);
403 let token_id = token_identifier.to_string();
404
405 let owner = self.owner_of_by_id(&token_id);
406
407 let is_owner = caller == owner;
408 let is_operator = !is_owner && self.data.operator(owner, caller);
409
410 if !is_owner && !is_operator {
411 self.revert(CEP78Error::InvalidTokenOwner);
412 }
413
414 self.ensure_not_burned(&token_id);
415
416 self.ensure_not_caller(spender);
417 self.data.approve(&token_id, spender);
418 self.emit_ces_event(Approval::new(owner, spender, token_id));
419 }
420
421 pub fn revoke(&mut self, token_id: Maybe<u64>, token_hash: Maybe<String>) {
425 self.ensure_not_minter_or_assigned();
426
427 let caller = self.caller();
428 let token_identifier = self.checked_token_identifier(token_id, token_hash);
429 let token_id = token_identifier.to_string();
430
431 let owner = self.owner_of_by_id(&token_id);
432 let is_owner = caller == owner;
433 let is_operator = !is_owner && self.data.operator(owner, caller);
434
435 if !is_owner && !is_operator {
436 self.revert(CEP78Error::InvalidTokenOwner);
437 }
438
439 self.ensure_not_burned(&token_id);
440 self.data.revoke(&token_id);
441
442 self.emit_ces_event(ApprovalRevoked::new(owner, token_id));
443 }
444
445 pub fn set_approval_for_all(&mut self, approve_all: bool, operator: Address) {
449 self.ensure_not_minter_or_assigned();
450 self.ensure_not_caller(operator);
451
452 let caller = self.caller();
453 self.data.set_operator(caller, operator, approve_all);
454
455 if let EventsMode::CES = self.settings.events_mode() {
456 if approve_all {
457 self.__env.emit_event(ApprovalForAll::new(caller, operator));
458 } else {
459 self.__env.emit_event(RevokedForAll::new(caller, operator));
460 }
461 }
462 }
463
464 pub fn is_approved_for_all(&mut self, token_owner: Address, operator: Address) -> bool {
466 self.data.operator(token_owner, operator)
467 }
468
469 pub fn owner_of(&self, token_id: Maybe<u64>, token_hash: Maybe<String>) -> Address {
472 let token_identifier = self.checked_token_identifier(token_id, token_hash);
473 self.owner_of_by_id(&token_identifier.to_string())
474 }
475
476 pub fn get_approved(
479 &mut self,
480 token_id: Maybe<u64>,
481 token_hash: Maybe<String>
482 ) -> Option<Address> {
483 let token_identifier: TokenIdentifier = self.checked_token_identifier(token_id, token_hash);
484 let token_id = token_identifier.to_string();
485
486 self.ensure_not_burned(&token_id);
487 self.data.approved(&token_id)
488 }
489
490 pub fn metadata(&self, token_id: Maybe<u64>, token_hash: Maybe<String>) -> String {
492 let token_identifier = self.checked_token_identifier(token_id, token_hash);
493 self.metadata.get_or_revert(&token_identifier)
494 }
495
496 pub fn set_token_metadata(
498 &mut self,
499 token_id: Maybe<u64>,
500 token_hash: Maybe<String>,
501 token_meta_data: String
502 ) {
503 let token_identifier = self.checked_token_identifier(token_id, token_hash);
504 let token_id = token_identifier.to_string();
505 self.ensure_caller_is_owner(&token_id);
506 self.set_token_metadata_unchecked(&token_id, token_meta_data);
507 }
508
509 pub fn balance_of(&self, token_owner: Address) -> u64 {
511 self.data.token_count(&token_owner)
512 }
513
514 pub fn register_owner(&mut self, token_owner: Maybe<Address>) -> String {
521 let ownership_mode = self.ownership_mode();
522 self.reverse_lookup
523 .register_owner(token_owner, ownership_mode);
524 "".to_string()
526 }
527}
528
529impl Cep78 {
530 #[inline]
531 fn caller(&self) -> Address {
532 self.__env.caller()
533 }
534
535 #[inline]
536 fn revert<E: Into<OdraError>>(&self, e: E) -> ! {
537 self.__env.revert(e)
538 }
539
540 #[inline]
541 pub fn is_minter_or_assigned(&self) -> bool {
542 matches!(
543 self.ownership_mode(),
544 OwnershipMode::Minter | OwnershipMode::Assigned
545 )
546 }
547
548 #[inline]
549 pub fn is_transferable_or_assigned(&self) -> bool {
550 matches!(
551 self.ownership_mode(),
552 OwnershipMode::Transferable | OwnershipMode::Assigned
553 )
554 }
555
556 #[inline]
557 pub fn ensure_not_minter_or_assigned(&self) {
558 if self.is_minter_or_assigned() {
559 self.revert(CEP78Error::InvalidOwnershipMode)
560 }
561 }
562
563 #[inline]
564 pub fn token_identifier(
565 &self,
566 token_id: Maybe<u64>,
567 token_hash: Maybe<String>
568 ) -> TokenIdentifier {
569 let env = self.env();
570 let identifier_mode: NFTIdentifierMode = self.metadata.get_identifier_mode();
571 match identifier_mode {
572 NFTIdentifierMode::Ordinal => TokenIdentifier::Index(token_id.unwrap(&env)),
573 NFTIdentifierMode::Hash => TokenIdentifier::Hash(token_hash.unwrap(&env))
574 }
575 }
576
577 pub fn token_id(&self, token_id: Maybe<u64>, token_hash: Maybe<String>) -> String {
578 let token_identifier = self.token_identifier(token_id, token_hash);
579 token_identifier.to_string()
580 }
581
582 #[inline]
583 pub fn checked_token_identifier(
584 &self,
585 token_id: Maybe<u64>,
586 token_hash: Maybe<String>
587 ) -> TokenIdentifier {
588 let identifier_mode: NFTIdentifierMode = self.metadata.get_identifier_mode();
589 let token_identifier = match identifier_mode {
590 NFTIdentifierMode::Ordinal => TokenIdentifier::Index(token_id.unwrap(&self.__env)),
591 NFTIdentifierMode::Hash => TokenIdentifier::Hash(token_hash.unwrap(&self.__env))
592 };
593
594 let number_of_minted_tokens = self.data.number_of_minted_tokens();
595 if let NFTIdentifierMode::Ordinal = identifier_mode {
596 if token_identifier.get_index().unwrap_or_revert(self) >= number_of_minted_tokens {
598 self.revert(CEP78Error::InvalidTokenIdentifier);
599 }
600 }
601 token_identifier
602 }
603
604 #[inline]
605 pub fn owner_of_by_id(&self, id: &str) -> Address {
606 match self.data.owner_of(id) {
607 Some(token_owner) => token_owner,
608 None => self
609 .env()
610 .revert(CEP78Error::MissingOwnerTokenIdentifierKey)
611 }
612 }
613
614 #[inline]
615 pub fn is_token_burned(&self, token_id: &str) -> bool {
616 self.data.is_burnt(token_id)
617 }
618
619 #[inline]
620 pub fn ensure_owner(&self, token_id: &str, address: &Address) {
621 let owner = self.owner_of_by_id(token_id);
622 if address != &owner {
623 self.revert(CEP78Error::InvalidAccount);
624 }
625 }
626
627 #[inline]
628 pub fn ensure_caller_is_owner(&self, token_id: &str) {
629 let owner = self.owner_of_by_id(token_id);
630 if self.caller() != owner {
631 self.revert(CEP78Error::InvalidTokenOwner);
632 }
633 }
634
635 #[inline]
636 pub fn ensure_not_burned(&self, token_id: &str) {
637 if self.is_token_burned(token_id) {
638 self.revert(CEP78Error::PreviouslyBurntToken);
639 }
640 }
641
642 #[inline]
643 pub fn ensure_not_caller(&self, address: Address) {
644 if self.caller() == address {
645 self.revert(CEP78Error::InvalidAccount);
646 }
647 }
648
649 #[inline]
650 pub fn ensure_caller(&self, address: Address) {
651 if self.caller() != address {
652 self.revert(CEP78Error::InvalidAccount);
653 }
654 }
655
656 #[inline]
657 pub fn emit_ces_event<T: ToBytes + EventInstance>(&self, event: T) {
658 let events_mode = self.settings.events_mode();
659 if let EventsMode::CES = events_mode {
660 self.env().emit_event(event);
661 }
662 }
663
664 #[inline]
665 pub fn ensure_burnable(&self) {
666 if let BurnMode::NonBurnable = self.settings.burn_mode() {
667 self.revert(CEP78Error::InvalidBurnMode)
668 }
669 }
670
671 #[inline]
672 pub fn ownership_mode(&self) -> OwnershipMode {
673 self.settings.ownership_mode()
674 }
675
676 #[inline]
677 pub fn verified_caller(&self) -> Address {
678 let holder_mode = self.settings.holder_mode();
679 let caller = self.caller();
680
681 match (caller, holder_mode) {
682 (Address::Account(_), NFTHolderMode::Contracts)
683 | (Address::Contract(_), NFTHolderMode::Accounts) => {
684 self.revert(CEP78Error::InvalidHolderMode);
685 }
686 _ => caller
687 }
688 }
689
690 pub fn token_exists_by_hash(&self, token_id: &str) -> bool {
692 self.data.owner_of(token_id).is_some() && !self.is_token_burned(token_id)
693 }
694
695 pub fn set_token_metadata_unchecked(&mut self, token_id: &String, token_meta_data: String) {
697 self.metadata
698 .ensure_mutability(CEP78Error::ForbiddenMetadataUpdate);
699 self.metadata.update_or_revert(&token_meta_data, token_id);
700 self.emit_ces_event(MetadataUpdated::new(
701 String::from(token_id),
702 token_meta_data
703 ));
704 }
705
706 pub fn burn_token_unchecked(&mut self, token_id: String, burner: Address) {
708 self.ensure_burnable();
709 let token_owner = self.owner_of_by_id(&token_id);
710 self.ensure_not_burned(&token_id);
711 self.data.mark_burnt(&token_id);
712 self.data.decrement_counter(&token_owner);
713 self.data.decrement_number_of_minted_tokens();
714 self.emit_ces_event(Burn::new(token_owner, token_id, burner));
715 }
716
717 pub fn get_collection_name(&self) -> String {
719 self.data.collection_name()
720 }
721
722 pub fn get_collection_symbol(&self) -> String {
724 self.data.collection_symbol()
725 }
726
727 pub fn is_whitelisted(&self, address: &Address) -> bool {
729 self.whitelist.is_whitelisted(address)
730 }
731
732 pub fn transfer_unchecked(
733 &mut self,
734 token_id: String,
735 owner: Address,
736 spender: Option<Address>,
737 reciepient: Address
738 ) {
739 self.data.set_owner(&token_id, reciepient);
740 self.data.decrement_counter(&owner);
741 self.data.increment_counter(&reciepient);
742 self.data.revoke(&token_id);
743
744 self.emit_ces_event(Transfer::new(owner, spender, reciepient, token_id));
745 }
746}
747
748#[odra::external_contract]
749pub trait TransferFilterContract {
750 fn can_transfer(
751 &self,
752 source_key: Address,
753 target_key: Address,
754 token_id: TokenIdentifier
755 ) -> TransferFilterContractResult;
756}
757
758#[odra::module]
759pub struct TestCep78 {
760 token: SubModule<Cep78>
761}
762
763#[odra::module]
764impl TestCep78 {
765 delegate! {
766 to self.token {
767 fn init(
768 &mut self,
769 collection_name: String,
770 collection_symbol: String,
771 total_token_supply: u64,
772 ownership_mode: OwnershipMode,
773 nft_kind: NFTKind,
774 identifier_mode: NFTIdentifierMode,
775 nft_metadata_kind: NFTMetadataKind,
776 metadata_mutability: MetadataMutability,
777 receipt_name: String,
778 allow_minting: Maybe<bool>,
779 minting_mode: Maybe<MintingMode>,
780 holder_mode: Maybe<NFTHolderMode>,
781 whitelist_mode: Maybe<WhitelistMode>,
782 acl_whitelist: Maybe<Vec<Address>>,
783 json_schema: Maybe<String>,
784 burn_mode: Maybe<BurnMode>,
785 operator_burn_mode: Maybe<bool>,
786 owner_reverse_lookup_mode: Maybe<OwnerReverseLookupMode>,
787 events_mode: Maybe<EventsMode>,
788 transfer_filter_contract_contract: Maybe<Address>,
789 additional_required_metadata: Maybe<Vec<NFTMetadataKind>>,
790 optional_metadata: Maybe<Vec<NFTMetadataKind>>
791 );
792 fn set_variables(
793 &mut self,
794 allow_minting: Maybe<bool>,
795 acl_whitelist: Maybe<Vec<Address>>,
796 operator_burn_mode: Maybe<bool>
797 );
798 fn mint(
799 &mut self,
800 token_owner: Address,
801 token_meta_data: String,
802 token_hash: Maybe<String>
803 );
804 fn burn(&mut self, token_id: Maybe<u64>, token_hash: Maybe<String>);
805 fn transfer(
806 &mut self,
807 token_id: Maybe<u64>,
808 token_hash: Maybe<String>,
809 source_key: Address,
810 target_key: Address
811 );
812 fn approve(&mut self, spender: Address, token_id: Maybe<u64>, token_hash: Maybe<String>);
813 fn revoke(&mut self, token_id: Maybe<u64>, token_hash: Maybe<String>);
814 fn set_approval_for_all(&mut self, approve_all: bool, operator: Address);
815 fn is_approved_for_all(&mut self, token_owner: Address, operator: Address) -> bool;
816 fn owner_of(&self, token_id: Maybe<u64>, token_hash: Maybe<String>) -> Address;
817 fn get_approved(
818 &mut self,
819 token_id: Maybe<u64>,
820 token_hash: Maybe<String>
821 ) -> Option<Address>;
822 fn metadata(&self, token_id: Maybe<u64>, token_hash: Maybe<String>) -> String;
823 fn set_token_metadata(
824 &mut self,
825 token_id: Maybe<u64>,
826 token_hash: Maybe<String>,
827 token_meta_data: String
828 );
829 fn balance_of(&self, token_owner: Address) -> u64;
830 fn register_owner(&mut self, token_owner: Maybe<Address>) -> String;
831 fn is_whitelisted(&self, address: &Address) -> bool;
832 }
833 }
834
835 pub fn get_whitelist_mode(&self) -> WhitelistMode {
836 self.token.whitelist.get_mode()
837 }
838
839 pub fn get_collection_name(&self) -> String {
840 self.token.data.collection_name()
841 }
842
843 pub fn get_collection_symbol(&self) -> String {
844 self.token.data.collection_symbol()
845 }
846
847 pub fn is_minting_allowed(&self) -> bool {
848 self.token.settings.allow_minting()
849 }
850
851 pub fn is_operator_burn_mode(&self) -> bool {
852 self.token.settings.operator_burn_mode()
853 }
854
855 pub fn get_total_supply(&self) -> u64 {
856 self.token.data.total_token_supply()
857 }
858
859 pub fn get_minting_mode(&self) -> MintingMode {
860 self.token.settings.minting_mode()
861 }
862
863 pub fn get_holder_mode(&self) -> NFTHolderMode {
864 self.token.settings.holder_mode()
865 }
866
867 pub fn get_number_of_minted_tokens(&self) -> u64 {
868 self.token.data.number_of_minted_tokens()
869 }
870
871 pub fn get_page(&self, page_number: u64) -> Vec<bool> {
872 let env = self.env();
873 let owner = env.caller();
874
875 let owner_key = utils::address_to_key(&owner);
876 let page_dict = format!("{PREFIX_PAGE_DICTIONARY}_{}", page_number);
877 env.get_dictionary_value(page_dict, owner_key.as_bytes())
878 .unwrap_or_revert_with(&self.env(), CEP78Error::InvalidPageNumber)
879 }
880
881 pub fn get_page_by_token_id(&self, token_id: u64) -> Vec<bool> {
882 let env = self.env();
883 let owner = env.caller();
884 let page_table_entry = token_id / PAGE_SIZE;
885
886 let page_dict = format!("{PREFIX_PAGE_DICTIONARY}_{}", page_table_entry);
887 let owner_key = utils::address_to_key(&owner);
888
889 env.get_dictionary_value(page_dict, owner_key.as_bytes())
890 .unwrap_or_revert_with(&env, CEP78Error::MissingPage)
891 }
892
893 pub fn get_page_by_token_hash(&self, token_hash: String) -> Vec<bool> {
894 let identifier = TokenIdentifier::Hash(token_hash);
895 let token_id = self
896 .token
897 .reverse_lookup
898 .get_token_index_checked(&identifier);
899 self.get_page_by_token_id(token_id)
900 }
901
902 pub fn get_page_table(&self) -> Vec<bool> {
903 self.token
904 .reverse_lookup
905 .get_page_table(&self.__env.caller(), CEP78Error::MissingPage)
906 }
907
908 pub fn get_metadata_by_kind(
909 &self,
910 kind: NFTMetadataKind,
911 token_id: Maybe<u64>,
912 token_hash: Maybe<String>
913 ) -> String {
914 let token_identifier = self.token.checked_token_identifier(token_id, token_hash);
915 self.token
916 .metadata
917 .get_metadata_by_kind(token_identifier.to_string(), &kind)
918 }
919
920 pub fn get_token_issuer(&self, token_id: Maybe<u64>, token_hash: Maybe<String>) -> Address {
921 let token_identifier = self.token.checked_token_identifier(token_id, token_hash);
922 self.token.data.issuer(&token_identifier.to_string())
923 }
924
925 pub fn token_burned(&self, token_id: Maybe<u64>, token_hash: Maybe<String>) -> bool {
926 let token_identifier = self.token.token_identifier(token_id, token_hash);
927 let token_id = token_identifier.to_string();
928 self.token.is_token_burned(&token_id)
929 }
930}