odra_modules/
cep95.rs

1#![allow(unused_variables, missing_docs)]
2
3use alloc::collections::BTreeMap;
4use odra::{
5    casper_types::{
6        bytesrepr::{Bytes, ToBytes},
7        U256
8    },
9    named_keys::{
10        base64_encoded_key_value_storage, compound_key_value_storage, single_value_storage
11    },
12    prelude::*,
13    ContractRef
14};
15
16/// Casper-compatible NFT interface
17pub trait CEP95Interface {
18    /// Returns a name of the NFT token/collection.
19    fn name(&self) -> String;
20
21    /// Returns a short symbol or abbreviation for the NFT token/collection.
22    fn symbol(&self) -> String;
23
24    /// Returns the number of NFTs owned by a given account or contract
25    ///
26    /// # Arguments
27    /// owner - The account to query.
28    ///
29    /// # Returns
30    /// The number of NFTs owned by the account.
31    fn balance_of(&self, owner: Address) -> U256;
32
33    /// Returns the owner of a specific NFT.
34    ///
35    /// # Arguments
36    /// token_id - The ID of the NFT.
37    ///
38    /// # Returns
39    /// The owner if it exists, else None.
40    fn owner_of(&self, token_id: U256) -> Option<Address>;
41
42    /// Performs a recipient check and transfers the ownership of an NFT.
43    ///
44    /// Reverts unless the contract caller is the current owner, an authorized
45    /// operator, or the approved spender for this NFT. Reverts if `from` is not
46    /// the current owner, or if `token_id` does not reference a valid NFT.
47    /// Once ownership is updated and a `Transfer` event is emitted, the function
48    /// checks whether `to` is a contract hash. If it is, the contract MUST call
49    /// `on_cep95_received` on `to` and revert the entire transfer if that call
50    /// is absent or returns any value other than `true`.
51    ///
52    /// # Arguments
53    /// from - The current owner of the NFT.
54    /// to - The new owner.
55    /// token_id - The NFT ID.
56    /// data - Optional payload to pass to a receiving contract.
57    fn safe_transfer_from(
58        &mut self,
59        from: Address,
60        to: Address,
61        token_id: U256,
62        data: Option<Bytes>
63    );
64
65    /// Transfers the ownership of an NFT without checking the recipient contract.
66    ///
67    /// # Arguments
68    /// from - The current owner of the NFT.
69    /// to - The new owner.
70    /// token_id - The NFT ID.
71    fn transfer_from(&mut self, from: Address, to: Address, token_id: U256);
72
73    /// Approves another account or contract to transfer a specific NFT.
74    ///
75    /// # Arguments
76    /// spender - The account or contract that will be granted approval.
77    /// token_id - The NFT ID.
78    fn approve(&mut self, spender: Address, token_id: U256);
79
80    /// Revokes approval for a specific NFT.
81    ///
82    /// # Arguments
83    /// token_id - The NFT ID to revoke approval for.
84    fn revoke_approval(&mut self, token_id: U256);
85
86    /// Gets the approved account or contract for a specific NFT.
87    ///
88    /// # Arguments
89    /// token_id - The ID of the NFT to check.
90    ///
91    /// # Returns
92    /// Option<Address> - The approved spender account if one exists, else None.
93    fn approved_for(&self, token_id: U256) -> Option<Address>;
94
95    /// Enables operator approval for all of the caller's NFTs.
96    ///
97    /// # Arguments
98    /// operator - The operator address to be approved.
99    fn approve_for_all(&mut self, operator: Address);
100
101    /// Revokes operator approval for all of the caller's NFTs.
102    ///
103    /// # Arguments
104    /// operator - The operator address to be revoked.
105    fn revoke_approval_for_all(&mut self, operator: Address);
106
107    /// Checks if an operator is approved to manage all NFTs of the owner.
108    ///
109    /// # Arguments
110    /// owner - The NFT owner's address.
111    /// operator - The operator to check.
112    ///
113    /// # Returns
114    /// True if the operator is approved for all NFTs, false otherwise.
115    fn is_approved_for_all(&self, owner: Address, operator: Address) -> bool;
116
117    /// Returns metadata for a given token ID.
118    ///
119    /// # Arguments
120    /// token_id - The ID of the NFT.
121    ///
122    /// # Returns
123    /// A vector of key-value pairs representing the metadata.
124    fn token_metadata(&self, token_id: U256) -> Vec<(String, String)>;
125}
126
127#[odra::module]
128/// Receiver interface
129struct CEP95Receiver;
130
131#[odra::module]
132impl CEP95Receiver {
133    /// Called after a `safe_transfer_from` completes its internal state update.
134    /// MUST return `true` to signal acceptance; returning `false` or reverting
135    /// causes the entire transfer to roll back.
136    ///
137    /// # Arguments
138    /// operator - The account (EOA or contract) that invoked `safe_transfer_from`.
139    /// from - The previous owner of `token_id`.
140    /// token_id - The NFT being transferred.
141    /// data - Opaque auxiliary data forwarded from the original call; may be `None` if no extra data was
142    /// supplied.
143    ///
144    /// # Returns
145    /// `true` to accept the NFT, anything else to reject.
146    #[allow(dead_code)]
147    pub fn on_cep95_received(
148        &mut self,
149        operator: &Address,
150        from: &Address,
151        token_id: &U256,
152        data: &Option<Bytes>
153    ) -> bool {
154        // This is a placeholder implementation. In a real contract, you would
155        // implement the logic to handle the received NFT here.
156        // For example, you might want to store the token ID and data in your contract's state.
157        // For now, we just return true to indicate acceptance.
158        true
159    }
160}
161
162const KEY_BALANCES: &str = "balances";
163const KEY_NAME: &str = "name";
164const KEY_SYMBOL: &str = "symbol";
165const KEY_APPROVED: &str = "approvals";
166const KEY_OPERATORS: &str = "operators";
167const KEY_METADATA: &str = "token_metadata";
168const KEY_OWNERS: &str = "owners";
169
170single_value_storage!(Cep95Name, String, KEY_NAME, Error::ValueNotSet);
171single_value_storage!(Cep95Symbol, String, KEY_SYMBOL, Error::ValueNotSet);
172base64_encoded_key_value_storage!(Cep95Balances, KEY_BALANCES, Address, U256);
173base64_encoded_key_value_storage!(Cep95Approvals, KEY_APPROVED, U256, Option<Address>);
174compound_key_value_storage!(Cep95Operators, KEY_OPERATORS, Address, bool);
175base64_encoded_key_value_storage!(Cep95Owners, KEY_OWNERS, U256, Option<Address>);
176base64_encoded_key_value_storage!(Cep95Metadata, KEY_METADATA, U256, BTreeMap<String, String>);
177
178/// Error enum for the CEP-95 contract.
179#[odra::odra_error]
180pub enum Error {
181    /// The value is not set.
182    ValueNotSet = 40_000,
183    /// The transfer failed.
184    TransferFailed = 40_001,
185    /// The token ID is invalid.
186    NotAnOwnerOrApproved = 40_002,
187    /// The approval is set to the current owner.
188    ApprovalToCurrentOwner = 40_003,
189    /// The caller is the same as the operator.
190    ApproveToCaller = 40_004,
191    /// The token ID is invalid.
192    InvalidTokenId = 40_005,
193    /// The token with the given ID already exists.
194    TokenAlreadyExists = 40_006
195}
196
197#[odra::event]
198/// Emitted when an NFT is minted
199pub struct Mint {
200    /// The address of the recipient.
201    pub to: Address,
202    /// The ID of the minted token.
203    pub token_id: U256
204}
205
206#[odra::event]
207/// Emitted when an NFT is burned
208pub struct Burn {
209    /// The address of the owner.
210    pub from: Address,
211    /// The ID of the burned token.
212    pub token_id: U256
213}
214
215#[odra::event]
216/// Emitted when an NFT is transferred
217pub struct Transfer {
218    /// The address of the previous owner.
219    pub from: Address,
220    /// The address of the new owner.
221    pub to: Address,
222    /// The ID of the transferred token.
223    pub token_id: U256
224}
225
226#[odra::event]
227/// Emitted when a specific NFT is approved to an account/contract
228pub struct Approval {
229    /// The address of the owner.
230    pub owner: Address,
231    /// The address of the approved spender.
232    pub spender: Address,
233    /// The ID of the approved token.
234    pub token_id: U256
235}
236
237#[odra::event]
238/// Emitted when a specific NFT approval is revoked from an account/contract
239pub struct RevokeApproval {
240    /// The address of the owner.
241    pub owner: Address,
242    /// The address of the revoked spender.
243    pub spender: Address,
244    /// The ID of the revoked token.
245    pub token_id: U256
246}
247
248#[odra::event]
249/// Emitted when an operator is approved for all NFTs of an owner
250pub struct ApprovalForAll {
251    /// The address of the owner.
252    pub owner: Address,
253    /// The address of the operator.
254    pub operator: Address
255}
256
257#[odra::event]
258/// Emitted when an operator approval is revoked for all NFTs of an owner
259pub struct RevokeApprovalForAll {
260    /// The address of the owner.
261    pub owner: Address,
262    /// The address of the operator.
263    pub operator: Address
264}
265
266#[odra::event]
267/// Emitted whenever on-chain metadata for a token is created or updated
268pub struct MetadataUpdate {
269    /// The ID of the token.
270    pub token_id: U256
271}
272
273#[odra::module(
274    events = [
275        Transfer,
276        Approval,
277        RevokeApproval,
278        ApprovalForAll,
279        RevokeApprovalForAll,
280        Mint,
281        Burn,
282        MetadataUpdate
283    ],
284    errors = Error
285)]
286/// A module representing a CEP-95 standard.
287pub struct Cep95 {
288    /// A submodule for the token name.
289    pub name: SubModule<Cep95Name>,
290    /// A submodule for the token symbol.
291    pub symbol: SubModule<Cep95Symbol>,
292    /// A submodule for the token balances mapping.
293    pub balances: SubModule<Cep95Balances>,
294    /// A submodule for the token owners mapping.
295    pub owners: SubModule<Cep95Owners>,
296    /// A submodule for the token approvals mapping.
297    pub approvals: SubModule<Cep95Approvals>,
298    /// A submodule for the token operators mapping.
299    pub operators: SubModule<Cep95Operators>,
300    /// A submodule for the token metadata mapping.
301    pub metadata: SubModule<Cep95Metadata>
302}
303
304#[odra::module]
305impl CEP95Interface for Cep95 {
306    fn name(&self) -> String {
307        self.name.get()
308    }
309
310    fn symbol(&self) -> String {
311        self.symbol.get()
312    }
313
314    fn balance_of(&self, owner: Address) -> U256 {
315        self.balances.get(&owner).unwrap_or_default()
316    }
317
318    fn owner_of(&self, token_id: U256) -> Option<Address> {
319        self.owners.get(&token_id).flatten()
320    }
321
322    fn safe_transfer_from(
323        &mut self,
324        from: Address,
325        to: Address,
326        token_id: U256,
327        data: Option<Bytes>
328    ) {
329        self.transfer_from(from, to, token_id);
330        if to.is_contract() {
331            let mut receiver = CEP95ReceiverContractRef::new(self.env(), to);
332            let caller = self.env().caller();
333            let result = receiver.on_cep95_received(&caller, &from, &token_id, &data);
334            if !result {
335                self.env().revert(Error::TransferFailed);
336            }
337        }
338    }
339
340    fn transfer_from(&mut self, from: Address, to: Address, token_id: U256) {
341        self.assert_exists(&token_id);
342
343        let caller = self.env().caller();
344        let previous_owner = self.raw_transfer(to, token_id);
345
346        // `from` must be the current owner.
347        if previous_owner != from {
348            self.env().revert(Error::NotAnOwnerOrApproved);
349        }
350
351        // Check if the caller is authorized to transfer the token.
352        // It can be either:
353        // - the owner,
354        // - an operator approved for all of the owner's tokens,
355        // - the approved spender for this specific token.
356        let is_authorized = previous_owner == caller
357            || self.is_approved_for_all(from, caller)
358            || self.is_spender(token_id, caller);
359
360        if !is_authorized {
361            self.env().revert(Error::NotAnOwnerOrApproved);
362        }
363    }
364
365    fn approve(&mut self, spender: Address, token_id: U256) {
366        let caller = self.env().caller();
367        let owner = self
368            .owner_of(token_id)
369            .unwrap_or_revert_with(self, Error::InvalidTokenId);
370        self.set_approve(token_id, owner, Some(spender));
371        self.env().emit_event(Approval {
372            owner,
373            spender,
374            token_id
375        });
376    }
377
378    fn revoke_approval(&mut self, token_id: U256) {
379        let spender = self
380            .approved_for(token_id)
381            .unwrap_or_revert_with(self, Error::ValueNotSet);
382        let owner = self
383            .owner_of(token_id)
384            .unwrap_or_revert_with(self, Error::InvalidTokenId);
385        self.set_approve(token_id, owner, None);
386        self.env().emit_event(RevokeApproval {
387            owner,
388            spender,
389            token_id
390        });
391    }
392
393    fn approved_for(&self, token_id: U256) -> Option<Address> {
394        self.assert_exists(&token_id);
395        self.approvals.get(&token_id).flatten()
396    }
397
398    fn approve_for_all(&mut self, operator: Address) {
399        let caller = self.env().caller();
400        self.set_approval_for_all(caller, operator, true);
401        self.env().emit_event(ApprovalForAll {
402            owner: caller,
403            operator
404        });
405    }
406
407    fn revoke_approval_for_all(&mut self, operator: Address) {
408        let caller = self.env().caller();
409        self.set_approval_for_all(caller, operator, false);
410        self.env().emit_event(RevokeApprovalForAll {
411            owner: caller,
412            operator
413        });
414    }
415
416    fn is_approved_for_all(&self, owner: Address, operator: Address) -> bool {
417        self.operators.get_or_default(&owner, &operator)
418    }
419
420    fn token_metadata(&self, token_id: U256) -> Vec<(String, String)> {
421        self.assert_exists(&token_id);
422        self.metadata
423            .get(&token_id)
424            .unwrap_or_default()
425            .into_iter()
426            .collect()
427    }
428}
429
430impl Cep95 {
431    /// Initializes the module with a name and symbol.
432    pub fn init(&mut self, name: String, symbol: String) {
433        // Initialize the name and symbol.
434        self.name.set(name);
435        self.symbol.set(symbol);
436
437        // Initialize dictionaries.
438        self.balances.init();
439        self.owners.init();
440        self.approvals.init();
441        self.operators.init();
442        self.metadata.init();
443    }
444
445    #[inline]
446    /// Asserts that the token ID exists.
447    /// Reverts with `Error::InvalidTokenId` if it does not.
448    pub fn assert_exists(&self, token_id: &U256) {
449        if !self.exists(token_id) {
450            self.env().revert(Error::InvalidTokenId);
451        }
452    }
453
454    /// Checks if the token ID exists.
455    #[inline]
456    pub fn exists(&self, token_id: &U256) -> bool {
457        self.owners.get(token_id).flatten().is_some()
458    }
459
460    /// Clears the approval for a specific token ID.
461    /// SECURITY: Do not expose this function publicly without proper access control.
462    #[inline]
463    pub fn clear_approval(&mut self, token_id: &U256) {
464        if self.approvals.get(token_id).is_some() {
465            self.approvals.set(token_id, None);
466        }
467    }
468
469    /// Mints a new NFT and assigns it to the specified address.
470    /// SECURITY: Do not expose this function publicly without proper access control.
471    pub fn raw_mint(&mut self, to: Address, token_id: U256, metadata: Vec<(String, String)>) {
472        if self.exists(&token_id) {
473            self.env().revert(Error::TokenAlreadyExists);
474        }
475
476        self.balances.set(&to, self.balance_of(to) + 1);
477        self.owners.set(&token_id, Some(to));
478        self.metadata.set(&token_id, BTreeMap::from_iter(metadata));
479
480        self.env().emit_event(Mint { to, token_id });
481    }
482
483    /// Burns an NFT, removing it from the owner's balance and the contract.
484    /// SECURITY: Do not expose this function publicly without proper access control.
485    pub fn raw_burn(&mut self, token_id: U256) {
486        self.assert_exists(&token_id);
487        let owner = self
488            .owner_of(token_id)
489            .unwrap_or_revert_with(self, Error::ValueNotSet);
490
491        self.clear_approval(&token_id);
492        self.balances.set(&owner, self.balance_of(owner) - 1);
493        self.owners.set(&token_id, None);
494        self.metadata.set(&token_id, Default::default());
495
496        self.env().emit_event(Burn {
497            from: owner,
498            token_id
499        });
500    }
501
502    /// Sets metadata for a specific token ID.
503    /// Replaces any existing metadata.
504    /// SECURITY: Do not expose this function publicly without proper access control.
505    pub fn set_metadata(&mut self, token_id: U256, metadata: Vec<(String, String)>) {
506        self.raw_update_metadata(token_id, metadata, BTreeMap::new());
507    }
508
509    /// Updates metadata for a specific token ID.
510    /// If a key already exists, its value will be updated.
511    /// If a key does not exist, it will be added.
512    /// The remaining keys will be preserved.
513    /// SECURITY: Do not expose this function publicly without proper access control.
514    pub fn update_metadata(&mut self, token_id: U256, metadata: Vec<(String, String)>) {
515        let current_metadata = self.metadata.get(&token_id).unwrap_or_default();
516        self.raw_update_metadata(token_id, metadata, current_metadata);
517    }
518
519    /// Transfers an NFT from one address to another without checking the recipient contract.
520    /// SECURITY: Do not expose this function publicly without proper access control.
521    pub fn raw_transfer(&mut self, to: Address, token_id: U256) -> Address {
522        let from = self
523            .owner_of(token_id)
524            .unwrap_or_revert_with(self, Error::InvalidTokenId);
525        self.balances.set(&from, self.balance_of(from) - 1);
526        self.balances.set(&to, self.balance_of(to) + 1);
527        self.owners.set(&token_id, Some(to));
528
529        self.env().emit_event(Transfer { from, to, token_id });
530        from
531    }
532
533    fn raw_update_metadata(
534        &mut self,
535        token_id: U256,
536        new_metadata: Vec<(String, String)>,
537        mut current_metadata: BTreeMap<String, String>
538    ) {
539        self.assert_exists(&token_id);
540        for (k, v) in new_metadata {
541            current_metadata.insert(k, v);
542        }
543        self.metadata.set(&token_id, current_metadata);
544        self.env().emit_event(MetadataUpdate { token_id });
545    }
546
547    #[inline]
548    fn set_approve(&mut self, token_id: U256, owner: Address, spender: Option<Address>) {
549        if Some(owner) == spender {
550            self.env().revert(Error::ApprovalToCurrentOwner);
551        }
552
553        let caller = self.env().caller();
554        if caller != owner && !self.is_approved_for_all(owner, caller) {
555            self.env().revert(Error::NotAnOwnerOrApproved);
556        }
557
558        self.approvals.set(&token_id, spender);
559    }
560
561    #[inline]
562    fn set_approval_for_all(&mut self, caller: Address, operator: Address, approved: bool) {
563        if caller == operator {
564            self.env().revert(Error::ApproveToCaller)
565        }
566
567        self.operators.set(&caller, &operator, approved);
568    }
569
570    #[inline]
571    fn is_spender(&self, token_id: U256, spender: Address) -> bool {
572        self.approved_for(token_id) == Some(spender)
573    }
574}
575
576mod utils {
577    #![allow(dead_code)]
578    use crate::access::Ownable;
579
580    use super::*;
581
582    #[odra::module]
583    pub(crate) struct BasicCep95 {
584        token: SubModule<Cep95>,
585        ownable: SubModule<Ownable>
586    }
587
588    #[odra::module]
589    impl BasicCep95 {
590        /// Initializes the contract with the given parameters.
591        pub fn init(&mut self, name: String, symbol: String) {
592            let owner = self.env().caller();
593            self.ownable.init(owner);
594            self.token.init(name, symbol);
595        }
596
597        delegate! {
598            to self.token {
599                fn name(&self) -> String;
600                fn symbol(&self) -> String;
601                fn balance_of(&self, owner: Address) -> U256;
602                fn owner_of(&self, token_id: U256) -> Option<Address>;
603                fn safe_transfer_from(&mut self, from: Address, to: Address, token_id: U256, data: Option<Bytes>);
604                fn transfer_from(&mut self, from: Address, to: Address, token_id: U256);
605                fn approve(&mut self, spender: Address, token_id: U256);
606                fn revoke_approval(&mut self, token_id: U256);
607                fn approved_for(&self, token_id: U256) -> Option<Address>;
608                fn approve_for_all(&mut self, operator: Address);
609                fn revoke_approval_for_all(&mut self, operator: Address);
610                fn is_approved_for_all(&self, owner: Address, operator: Address) -> bool;
611                fn token_metadata(&self, token_id: U256) -> Vec<(String, String)>;
612            }
613        }
614
615        pub fn mint(&mut self, to: Address, token_id: U256, metadata: Vec<(String, String)>) {
616            self.assert_owner();
617            self.token.raw_mint(to, token_id, metadata);
618        }
619
620        pub fn burn(&mut self, token_id: U256) {
621            self.assert_owner();
622            self.token.raw_burn(token_id);
623        }
624
625        pub fn set_metadata(&mut self, token_id: U256, metadata: Vec<(String, String)>) {
626            self.assert_owner();
627            self.token.set_metadata(token_id, metadata);
628        }
629
630        pub fn update_metadata(&mut self, token_id: U256, metadata: Vec<(String, String)>) {
631            self.assert_owner();
632            self.token.update_metadata(token_id, metadata);
633        }
634
635        pub fn assert_owner(&self) {
636            self.ownable.assert_owner(&self.env().caller());
637        }
638    }
639
640    #[odra::module]
641    pub(crate) struct NFTReceiver {
642        #[allow(clippy::type_complexity)]
643        last_call_data: Var<((Address, Address), (U256, Option<Bytes>))>
644    }
645
646    #[odra::module]
647    impl NFTReceiver {
648        #[allow(dead_code)]
649        pub fn on_cep95_received(
650            &mut self,
651            operator: Address,
652            from: Address,
653            token_id: U256,
654            data: Option<Bytes>
655        ) -> bool {
656            self.last_call_data
657                .set(((operator, from), (token_id, data.clone())));
658            true
659        }
660
661        #[allow(dead_code)]
662        pub fn last_call_result(&self) -> ((Address, Address), (U256, Option<Bytes>)) {
663            self.last_call_data.get().unwrap_or_revert(self)
664        }
665    }
666
667    #[odra::module]
668    pub(crate) struct RejectingNFTReceiver;
669
670    #[odra::module]
671    impl RejectingNFTReceiver {
672        #[allow(dead_code)]
673        pub fn on_cep95_received(
674            &mut self,
675            operator: Address,
676            from: Address,
677            token_id: U256,
678            data: Option<Bytes>
679        ) -> bool {
680            false
681        }
682    }
683
684    #[odra::module]
685    pub(crate) struct BasicContract;
686
687    #[odra::module]
688    impl BasicContract {}
689}
690
691#[cfg(test)]
692mod tests {
693    use super::*;
694    use crate::cep95::utils::*;
695    use odra::{
696        host::{Deployer, HostEnv, NoArgs},
697        prelude::Addressable,
698        VmError
699    };
700    use odra_test;
701
702    fn setup() -> (HostEnv, BasicCep95HostRef) {
703        let env = odra_test::env();
704        let cep95 = BasicCep95::try_deploy(
705            &env,
706            BasicCep95InitArgs {
707                name: "TestToken".to_string(),
708                symbol: "TT".to_string()
709            }
710        )
711        .unwrap();
712        (env, cep95)
713    }
714
715    #[test]
716    fn test_deploy() {
717        let env = odra_test::env();
718        let cep95 = BasicCep95::try_deploy(
719            &env,
720            BasicCep95InitArgs {
721                name: "TestToken".to_string(),
722                symbol: "TT".to_string()
723            }
724        );
725        assert!(cep95.is_ok());
726    }
727
728    #[test]
729    fn test_name() {
730        let (env, cep95) = setup();
731        let name = cep95.name();
732        assert_eq!(name, "TestToken");
733    }
734
735    #[test]
736    fn test_symbol() {
737        let (env, cep95) = setup();
738        let symbol = cep95.symbol();
739        assert_eq!(symbol, "TT");
740    }
741
742    #[test]
743    fn test_mint() {
744        let (env, mut cep95) = setup();
745        let owner = env.caller();
746
747        let token_id = U256::from(1);
748        let metadata = vec![("key".to_string(), "value".to_string())];
749        cep95.mint(owner, token_id, metadata);
750
751        assert_eq!(cep95.balance_of(owner), U256::from(1));
752        assert_eq!(cep95.owner_of(token_id), Some(owner));
753        assert_eq!(
754            cep95.token_metadata(token_id),
755            vec![("key".to_string(), "value".to_string())]
756        );
757        assert!(env.emitted(&cep95, "Mint"));
758    }
759
760    #[test]
761    fn test_minting_existing_token() {
762        let (env, mut cep95) = setup();
763        let owner = env.caller();
764
765        let token_id = U256::from(1);
766        let metadata = vec![("key".to_string(), "value".to_string())];
767        cep95.mint(owner, token_id, metadata.clone());
768
769        let result = cep95.try_mint(owner, token_id, metadata);
770        assert_eq!(result, Err(Error::TokenAlreadyExists.into()));
771    }
772
773    #[test]
774    fn test_mint_many_tokens() {
775        let (env, mut cep95) = setup();
776        let owner = env.caller();
777
778        let token_id1 = U256::from(1);
779        let metadata1 = vec![("key1".to_string(), "value1".to_string())];
780        cep95.mint(owner, token_id1, metadata1);
781
782        let token_id2 = U256::from(2);
783        let metadata2 = vec![("key2".to_string(), "value2".to_string())];
784        cep95.mint(owner, token_id2, metadata2);
785
786        assert_eq!(cep95.balance_of(owner), U256::from(2));
787        assert_eq!(cep95.owner_of(token_id1), Some(owner));
788        assert_eq!(cep95.owner_of(token_id2), Some(owner));
789    }
790
791    #[test]
792    fn test_burn() {
793        let (env, mut cep95) = setup();
794        let owner = env.caller();
795
796        let token_id = U256::from(1);
797        let metadata = vec![("key".to_string(), "value".to_string())];
798        cep95.mint(owner, token_id, metadata);
799        cep95.burn(token_id);
800
801        assert_eq!(cep95.balance_of(owner), U256::from(0));
802        assert_eq!(cep95.owner_of(token_id), None);
803        assert_eq!(
804            cep95.try_token_metadata(token_id),
805            Err(Error::InvalidTokenId.into())
806        );
807        assert!(env.emitted(&cep95, "Burn"));
808    }
809
810    #[test]
811    fn test_burn_non_existing_token() {
812        let (env, mut cep95) = setup();
813        let owner = env.caller();
814
815        let token_id = U256::from(1);
816        let result = cep95.try_burn(token_id);
817        assert_eq!(result, Err(Error::InvalidTokenId.into()));
818    }
819
820    #[test]
821    fn test_safe_transfer_to_receiver() {
822        let (env, mut cep95) = setup();
823        let nft_receiver = NFTReceiver::deploy(&env, NoArgs);
824        let recipient = nft_receiver.address();
825        let owner = env.caller();
826
827        let token_id = U256::from(1);
828        let metadata = vec![("key".to_string(), "value".to_string())];
829        cep95.mint(owner, token_id, metadata);
830
831        cep95.safe_transfer_from(owner, recipient, token_id, None);
832
833        assert_eq!(cep95.balance_of(owner), U256::from(0));
834        assert_eq!(cep95.balance_of(recipient), U256::from(1));
835        assert_eq!(
836            nft_receiver.last_call_result(),
837            ((owner, owner), (token_id, None))
838        );
839    }
840
841    #[test]
842    fn test_safe_transfer_to_non_receiver() {
843        let (env, mut cep95) = setup();
844        let contract = BasicContract::deploy(&env, NoArgs);
845        let recipient = contract.address();
846        let owner = env.caller();
847
848        let token_id = U256::from(1);
849        let metadata = vec![("key".to_string(), "value".to_string())];
850        cep95.mint(owner, token_id, metadata);
851
852        let result = cep95.try_safe_transfer_from(owner, recipient, token_id, None);
853        assert_eq!(
854            result,
855            Err(OdraError::VmError(VmError::NoSuchMethod(
856                "on_cep95_received".to_string()
857            )))
858        );
859
860        assert_eq!(cep95.balance_of(owner), U256::from(1));
861        assert_eq!(cep95.balance_of(recipient), U256::from(0));
862    }
863
864    #[test]
865    fn test_safe_transfer_to_rejecting_receiver() {
866        let (env, mut cep95) = setup();
867
868        let owner = env.get_account(0);
869        let contract = RejectingNFTReceiver::deploy(&env, NoArgs);
870        let recipient = contract.address();
871
872        let token_id = U256::from(1);
873        let metadata = vec![("key".to_string(), "value".to_string())];
874        cep95.mint(owner, token_id, metadata);
875
876        let result = cep95.try_safe_transfer_from(owner, recipient, token_id, None);
877        assert_eq!(result, Err(Error::TransferFailed.into()));
878
879        assert_eq!(cep95.balance_of(owner), U256::from(1));
880        assert_eq!(cep95.balance_of(recipient), U256::from(0));
881    }
882
883    #[test]
884    fn test_transfer() {
885        let (env, mut cep95) = setup();
886
887        let owner = env.get_account(0);
888        let recipient = env.get_account(10);
889
890        let token_id = U256::from(1);
891        let metadata = vec![("key".to_string(), "value".to_string())];
892        cep95.mint(owner, token_id, metadata);
893        cep95.transfer_from(owner, recipient, token_id);
894
895        assert_eq!(cep95.balance_of(owner), U256::from(0));
896        assert_eq!(cep95.balance_of(recipient), U256::from(1));
897        assert!(env.emitted(&cep95, "Transfer"));
898    }
899
900    #[test]
901    fn test_transfer_non_existing_token() {
902        let (env, mut cep95) = setup();
903
904        let owner = env.get_account(0);
905        let recipient = env.get_account(10);
906
907        let token_id = U256::from(1);
908        let metadata = vec![("key".to_string(), "value".to_string())];
909        cep95.mint(owner, token_id, metadata);
910
911        let result = cep95.try_transfer_from(owner, recipient, U256::from(2));
912        assert_eq!(result, Err(Error::InvalidTokenId.into()));
913    }
914
915    #[test]
916    fn test_transfer_from_non_owner() {
917        let (env, mut cep95) = setup();
918
919        let recipient = env.get_account(10);
920        let non_owner = env.get_account(11);
921        let owner = env.get_account(0);
922
923        let token_id = U256::from(1);
924        let metadata = vec![("key".to_string(), "value".to_string())];
925        cep95.mint(owner, token_id, metadata);
926
927        let result = cep95.try_transfer_from(non_owner, recipient, token_id);
928        assert_eq!(result, Err(Error::NotAnOwnerOrApproved.into()));
929        assert_eq!(cep95.balance_of(owner), U256::from(1));
930        assert_eq!(cep95.balance_of(recipient), U256::from(0));
931    }
932
933    #[test]
934    fn test_approve() {
935        let (env, mut cep95) = setup();
936        let owner = env.caller();
937
938        let token_id = U256::from(1);
939        let metadata = vec![("key".to_string(), "value".to_string())];
940        cep95.mint(owner, token_id, metadata);
941
942        let spender = env.get_account(10);
943        cep95.approve(spender, token_id);
944
945        assert_eq!(cep95.approved_for(token_id), Some(spender));
946        assert!(env.emitted_event(
947            &cep95,
948            Approval {
949                owner,
950                spender,
951                token_id
952            }
953        ));
954    }
955
956    #[test]
957    fn test_approve_by_non_owner() {
958        let (env, mut cep95) = setup();
959        let owner = env.get_account(0);
960        let non_owner = env.get_account(11);
961
962        let token_id = U256::from(1);
963        let metadata = vec![("key".to_string(), "value".to_string())];
964        cep95.mint(owner, token_id, metadata);
965
966        let spender = env.get_account(10);
967        env.set_caller(non_owner);
968        let result = cep95.try_approve(spender, token_id);
969        assert_eq!(result, Err(Error::NotAnOwnerOrApproved.into()));
970    }
971
972    #[test]
973    fn test_approve_by_spender() {
974        let (env, mut cep95) = setup();
975        let owner = env.get_account(0);
976        let spender = env.get_account(10);
977
978        let token_id = U256::from(1);
979        let metadata = vec![("key".to_string(), "value".to_string())];
980        cep95.approve_for_all(spender);
981        cep95.mint(owner, token_id, metadata);
982
983        env.set_caller(spender);
984        let result = cep95.try_approve(spender, token_id);
985        assert!(result.is_ok());
986        assert!(env.emitted_event(
987            &cep95,
988            Approval {
989                owner,
990                spender,
991                token_id
992            }
993        ));
994    }
995
996    #[test]
997    fn test_transfer_by_approved() {
998        let (env, mut cep95) = setup();
999        let owner = env.get_account(0);
1000        let recipient = env.get_account(10);
1001
1002        let token_id = U256::from(1);
1003        let metadata = vec![("key".to_string(), "value".to_string())];
1004        cep95.mint(owner, token_id, metadata);
1005
1006        let spender = env.get_account(11);
1007        cep95.approve(spender, token_id);
1008        env.set_caller(spender);
1009        cep95.transfer_from(owner, recipient, token_id);
1010
1011        assert_eq!(cep95.balance_of(owner), U256::from(0));
1012        assert_eq!(cep95.balance_of(recipient), U256::from(1));
1013        assert!(env.emitted_event(
1014            &cep95,
1015            Transfer {
1016                from: owner,
1017                to: recipient,
1018                token_id
1019            }
1020        ));
1021    }
1022
1023    #[test]
1024    fn test_approve_for_all() {
1025        let (env, mut cep95) = setup();
1026        let owner = env.caller();
1027        let operator = env.get_account(10);
1028
1029        cep95.approve_for_all(operator);
1030
1031        assert!(cep95.is_approved_for_all(owner, operator));
1032        assert!(env.emitted(&cep95, "ApprovalForAll"));
1033    }
1034
1035    #[test]
1036    fn test_revoke_approval_for_all() {
1037        let (env, mut cep95) = setup();
1038        let owner = env.caller();
1039        let operator = env.get_account(10);
1040
1041        cep95.approve_for_all(operator);
1042        cep95.revoke_approval_for_all(operator);
1043
1044        assert!(!cep95.is_approved_for_all(owner, operator));
1045        assert!(env.emitted(&cep95, "RevokeApprovalForAll"));
1046    }
1047
1048    #[test]
1049    fn test_approve_for_all_self() {
1050        let (env, mut cep95) = setup();
1051        let owner = env.caller();
1052
1053        let result = cep95.try_approve_for_all(owner);
1054        assert_eq!(result, Err(Error::ApproveToCaller.into()));
1055    }
1056
1057    #[test]
1058    fn test_revoke_approval() {
1059        let (env, mut cep95) = setup();
1060        let owner = env.caller();
1061
1062        let token_id = U256::from(1);
1063        let metadata = vec![("key".to_string(), "value".to_string())];
1064        cep95.mint(owner, token_id, metadata);
1065
1066        let spender = env.get_account(10);
1067        cep95.approve(spender, token_id);
1068        cep95.revoke_approval(token_id);
1069
1070        assert_eq!(cep95.approved_for(token_id), None);
1071        assert!(env.emitted(&cep95, "RevokeApproval"));
1072    }
1073
1074    #[test]
1075    fn test_revoke_approval_by_spender() {
1076        let (env, mut cep95) = setup();
1077        let owner = env.caller();
1078        let spender = env.get_account(10);
1079
1080        let token_id = U256::from(1);
1081        let metadata = vec![("key".to_string(), "value".to_string())];
1082        cep95.approve_for_all(spender);
1083        cep95.mint(owner, token_id, metadata);
1084        cep95.approve(spender, token_id);
1085
1086        env.set_caller(spender);
1087        cep95.revoke_approval(token_id);
1088
1089        assert_eq!(cep95.approved_for(token_id), None);
1090        assert!(env.emitted_event(
1091            &cep95,
1092            RevokeApproval {
1093                owner,
1094                spender,
1095                token_id
1096            }
1097        ));
1098    }
1099
1100    #[test]
1101    fn revoke_non_existing_approval() {
1102        let (env, mut cep95) = setup();
1103        let owner = env.caller();
1104
1105        let token_id = U256::from(1);
1106        let metadata = vec![("key".to_string(), "value".to_string())];
1107        cep95.mint(owner, token_id, metadata);
1108
1109        let result = cep95.try_revoke_approval(token_id);
1110        assert_eq!(result, Err(Error::ValueNotSet.into()));
1111    }
1112
1113    #[test]
1114    fn test_revoke_approval_by_non_owner() {
1115        let (env, mut cep95) = setup();
1116        let owner = env.get_account(0);
1117        let non_owner = env.get_account(11);
1118
1119        let token_id = U256::from(1);
1120        let metadata = vec![("key".to_string(), "value".to_string())];
1121        cep95.mint(owner, token_id, metadata);
1122
1123        let spender = env.get_account(10);
1124        cep95.approve(spender, token_id);
1125        env.set_caller(non_owner);
1126        let result = cep95.try_revoke_approval(token_id);
1127        assert_eq!(result, Err(Error::NotAnOwnerOrApproved.into()));
1128    }
1129
1130    #[test]
1131    fn test_metadata() {
1132        let (env, mut cep95) = setup();
1133        let owner = env.caller();
1134
1135        let token_id = U256::from(1);
1136        let metadata = vec![("key".to_string(), "value".to_string())];
1137        cep95.mint(owner, token_id, metadata);
1138
1139        assert_eq!(
1140            cep95.token_metadata(token_id),
1141            vec![("key".to_string(), "value".to_string())]
1142        );
1143    }
1144
1145    #[test]
1146    fn test_transfer_by_operator() {
1147        let (env, mut cep95) = setup();
1148        let owner = env.caller();
1149        let recipient = env.get_account(10);
1150        let operator = env.get_account(11);
1151
1152        let token_id1 = U256::from(1);
1153        let metadata = vec![("key".to_string(), "value".to_string())];
1154        cep95.mint(owner, token_id1, metadata.clone());
1155
1156        cep95.approve_for_all(operator);
1157        let token_id2 = U256::from(2);
1158        cep95.mint(owner, token_id2, metadata);
1159
1160        env.set_caller(operator);
1161        cep95.transfer_from(owner, recipient, token_id1);
1162        cep95.transfer_from(owner, recipient, token_id2);
1163
1164        assert_eq!(cep95.balance_of(owner), U256::from(0));
1165        assert_eq!(cep95.balance_of(recipient), U256::from(2));
1166    }
1167
1168    #[test]
1169    fn test_update_metadata() {
1170        let (env, mut cep95) = setup();
1171        let owner = env.caller();
1172
1173        let token_id = U256::from(1);
1174        let metadata = vec![
1175            ("age".to_string(), "30".to_string()),
1176            ("name".to_string(), "Alice".to_string()),
1177        ];
1178        cep95.mint(owner, token_id, metadata);
1179
1180        let new_metadata = vec![("name".to_string(), "Bob".to_string())];
1181        cep95.update_metadata(token_id, new_metadata);
1182
1183        assert_eq!(
1184            cep95.token_metadata(token_id),
1185            vec![
1186                ("age".to_string(), "30".to_string()),
1187                ("name".to_string(), "Bob".to_string()),
1188            ]
1189        );
1190        assert!(env.emitted(&cep95, "MetadataUpdate"));
1191    }
1192
1193    #[test]
1194    fn test_set_metadata() {
1195        let (env, mut cep95) = setup();
1196        let owner = env.caller();
1197
1198        let token_id = U256::from(1);
1199        let metadata = vec![
1200            ("age".to_string(), "30".to_string()),
1201            ("name".to_string(), "Alice".to_string()),
1202        ];
1203        cep95.mint(owner, token_id, metadata);
1204
1205        let new_metadata = vec![("name".to_string(), "Bob".to_string())];
1206        cep95.set_metadata(token_id, new_metadata);
1207
1208        assert_eq!(
1209            cep95.token_metadata(token_id),
1210            vec![("name".to_string(), "Bob".to_string())]
1211        );
1212        assert!(env.emitted(&cep95, "MetadataUpdate"));
1213    }
1214}