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 owner = self
345            .owner_of(token_id)
346            .unwrap_or_revert_with(self, Error::ValueNotSet);
347        // Only the owner or an approved spender can transfer the token.
348        if (owner != from || owner != caller) && !self.is_approved_for_all(from, caller) {
349            if let Some(approved) = self.approved_for(token_id) {
350                if approved != caller {
351                    self.env().revert(Error::NotAnOwnerOrApproved);
352                }
353            } else {
354                self.env().revert(Error::NotAnOwnerOrApproved);
355            }
356        }
357
358        self.raw_transfer_from(from, to, token_id);
359    }
360
361    fn approve(&mut self, spender: Address, token_id: U256) {
362        let caller = self.env().caller();
363        self.set_approve(token_id, Some(spender));
364        self.env().emit_event(Approval {
365            owner: caller,
366            spender,
367            token_id
368        });
369    }
370
371    fn revoke_approval(&mut self, token_id: U256) {
372        let spender = self
373            .approved_for(token_id)
374            .unwrap_or_revert_with(self, Error::ValueNotSet);
375        self.set_approve(token_id, None);
376        self.env().emit_event(RevokeApproval {
377            owner: self.env().caller(),
378            spender,
379            token_id
380        });
381    }
382
383    fn approved_for(&self, token_id: U256) -> Option<Address> {
384        self.assert_exists(&token_id);
385        self.approvals.get(&token_id).flatten()
386    }
387
388    fn approve_for_all(&mut self, operator: Address) {
389        let caller = self.env().caller();
390        self.set_approval_for_all(caller, operator, true);
391        self.env().emit_event(ApprovalForAll {
392            owner: caller,
393            operator
394        });
395    }
396
397    fn revoke_approval_for_all(&mut self, operator: Address) {
398        let caller = self.env().caller();
399        self.set_approval_for_all(caller, operator, false);
400        self.env().emit_event(RevokeApprovalForAll {
401            owner: caller,
402            operator
403        });
404    }
405
406    fn is_approved_for_all(&self, owner: Address, operator: Address) -> bool {
407        self.operators.get_or_default(&owner, &operator)
408    }
409
410    fn token_metadata(&self, token_id: U256) -> Vec<(String, String)> {
411        self.assert_exists(&token_id);
412        self.metadata
413            .get(&token_id)
414            .unwrap_or_default()
415            .into_iter()
416            .collect()
417    }
418}
419
420impl Cep95 {
421    /// Initializes the module with a name and symbol.
422    pub fn init(&mut self, name: String, symbol: String) {
423        // Initialize the name and symbol.
424        self.name.set(name);
425        self.symbol.set(symbol);
426
427        // Initialize dictionaries.
428        self.balances.init();
429        self.owners.init();
430        self.approvals.init();
431        self.operators.init();
432        self.metadata.init();
433    }
434
435    #[inline]
436    /// Asserts that the token ID exists.
437    /// Reverts with `Error::InvalidTokenId` if it does not.
438    pub fn assert_exists(&self, token_id: &U256) {
439        if !self.exists(token_id) {
440            self.env().revert(Error::InvalidTokenId);
441        }
442    }
443
444    /// Checks if the token ID exists.
445    #[inline]
446    pub fn exists(&self, token_id: &U256) -> bool {
447        self.owners.get(token_id).flatten().is_some()
448    }
449
450    /// Clears the approval for a specific token ID.
451    #[inline]
452    pub fn clear_approval(&mut self, token_id: &U256) {
453        if self.approvals.get(token_id).is_some() {
454            self.approvals.set(token_id, None);
455        }
456    }
457
458    /// Mints a new NFT and assigns it to the specified address.
459    pub fn mint(&mut self, to: Address, token_id: U256, metadata: Vec<(String, String)>) {
460        if self.exists(&token_id) {
461            self.env().revert(Error::TokenAlreadyExists);
462        }
463
464        self.balances.set(&to, self.balance_of(to) + 1);
465        self.owners.set(&token_id, Some(to));
466        self.metadata.set(&token_id, BTreeMap::from_iter(metadata));
467
468        self.env().emit_event(Mint { to, token_id });
469    }
470
471    /// Burns an NFT, removing it from the owner's balance and the contract.
472    pub fn burn(&mut self, token_id: U256) {
473        self.assert_exists(&token_id);
474        let owner = self
475            .owner_of(token_id)
476            .unwrap_or_revert_with(self, Error::ValueNotSet);
477
478        self.clear_approval(&token_id);
479        self.balances.set(&owner, self.balance_of(owner) - 1);
480        self.owners.set(&token_id, None);
481        self.metadata.set(&token_id, Default::default());
482
483        self.env().emit_event(Burn {
484            from: owner,
485            token_id
486        });
487    }
488
489    /// Sets metadata for a specific token ID.
490    /// Replaces any existing metadata.
491    pub fn set_metadata(&mut self, token_id: U256, metadata: Vec<(String, String)>) {
492        self.raw_update_metadata(token_id, metadata, BTreeMap::new());
493    }
494
495    /// Updates metadata for a specific token ID.
496    /// If a key already exists, its value will be updated.
497    /// If a key does not exist, it will be added.
498    /// The remaining keys will be preserved.
499    pub fn update_metadata(&mut self, token_id: U256, metadata: Vec<(String, String)>) {
500        let current_metadata = self.metadata.get(&token_id).unwrap_or_default();
501        self.raw_update_metadata(token_id, metadata, current_metadata);
502    }
503
504    /// Transfers an NFT from one address to another without checking the recipient contract.
505    pub fn raw_transfer_from(&mut self, from: Address, to: Address, token_id: U256) {
506        self.clear_approval(&token_id);
507        self.balances.set(&from, self.balance_of(from) - 1);
508        self.balances.set(&to, self.balance_of(to) + 1);
509        self.owners.set(&token_id, Some(to));
510
511        self.env().emit_event(Transfer { from, to, token_id });
512    }
513
514    fn raw_update_metadata(
515        &mut self,
516        token_id: U256,
517        new_metadata: Vec<(String, String)>,
518        mut current_metadata: BTreeMap<String, String>
519    ) {
520        self.assert_exists(&token_id);
521        for (k, v) in new_metadata {
522            current_metadata.insert(k, v);
523        }
524        self.metadata.set(&token_id, current_metadata);
525        self.env().emit_event(MetadataUpdate { token_id });
526    }
527
528    #[inline]
529    fn set_approve(&mut self, token_id: U256, spender: Option<Address>) {
530        let owner = self
531            .owner_of(token_id)
532            .unwrap_or_revert_with(self, Error::ValueNotSet);
533        let caller = self.env().caller();
534
535        if Some(owner) == spender {
536            self.env().revert(Error::ApprovalToCurrentOwner);
537        }
538
539        if caller != owner && !self.is_approved_for_all(owner, caller) {
540            self.env().revert(Error::NotAnOwnerOrApproved);
541        }
542
543        self.approvals.set(&token_id, spender);
544    }
545
546    #[inline]
547    fn set_approval_for_all(&mut self, caller: Address, operator: Address, approved: bool) {
548        if caller == operator {
549            self.env().revert(Error::ApproveToCaller)
550        }
551
552        self.operators.set(&caller, &operator, approved);
553    }
554}
555
556mod utils {
557    #![allow(dead_code)]
558    use super::*;
559
560    #[odra::module]
561    pub(crate) struct BasicCep95 {
562        token: SubModule<Cep95>
563    }
564
565    #[odra::module]
566    impl BasicCep95 {
567        /// Initializes the contract with the given parameters.
568        pub fn init(&mut self, name: String, symbol: String) {
569            self.token.init(name, symbol);
570        }
571
572        delegate! {
573            to self.token {
574                fn name(&self) -> String;
575                fn symbol(&self) -> String;
576                fn balance_of(&self, owner: Address) -> U256;
577                fn owner_of(&self, token_id: U256) -> Option<Address>;
578                fn safe_transfer_from(&mut self, from: Address, to: Address, token_id: U256, data: Option<Bytes>);
579                fn transfer_from(&mut self, from: Address, to: Address, token_id: U256);
580                fn approve(&mut self, spender: Address, token_id: U256);
581                fn revoke_approval(&mut self, token_id: U256);
582                fn approved_for(&self, token_id: U256) -> Option<Address>;
583                fn approve_for_all(&mut self, operator: Address);
584                fn revoke_approval_for_all(&mut self, operator: Address);
585                fn is_approved_for_all(&self, owner: Address, operator: Address) -> bool;
586                fn token_metadata(&self, token_id: U256) -> Vec<(String, String)>;
587            }
588        }
589
590        pub fn mint(&mut self, to: Address, token_id: U256, metadata: Vec<(String, String)>) {
591            self.token.mint(to, token_id, metadata);
592        }
593
594        pub fn burn(&mut self, token_id: U256) {
595            self.token.burn(token_id);
596        }
597
598        pub fn set_metadata(&mut self, token_id: U256, metadata: Vec<(String, String)>) {
599            self.token.set_metadata(token_id, metadata);
600        }
601
602        pub fn update_metadata(&mut self, token_id: U256, metadata: Vec<(String, String)>) {
603            self.token.update_metadata(token_id, metadata);
604        }
605    }
606
607    #[odra::module]
608    pub(crate) struct NFTReceiver {
609        #[allow(clippy::type_complexity)]
610        last_call_data: Var<((Address, Address), (U256, Option<Bytes>))>
611    }
612
613    #[odra::module]
614    impl NFTReceiver {
615        #[allow(dead_code)]
616        pub fn on_cep95_received(
617            &mut self,
618            operator: Address,
619            from: Address,
620            token_id: U256,
621            data: Option<Bytes>
622        ) -> bool {
623            self.last_call_data
624                .set(((operator, from), (token_id, data.clone())));
625            true
626        }
627
628        #[allow(dead_code)]
629        pub fn last_call_result(&self) -> ((Address, Address), (U256, Option<Bytes>)) {
630            self.last_call_data.get().unwrap_or_revert(self)
631        }
632    }
633
634    #[odra::module]
635    pub(crate) struct RejectingNFTReceiver;
636
637    #[odra::module]
638    impl RejectingNFTReceiver {
639        #[allow(dead_code)]
640        pub fn on_cep95_received(
641            &mut self,
642            operator: Address,
643            from: Address,
644            token_id: U256,
645            data: Option<Bytes>
646        ) -> bool {
647            false
648        }
649    }
650
651    #[odra::module]
652    pub(crate) struct BasicContract;
653
654    #[odra::module]
655    impl BasicContract {}
656}
657
658#[cfg(test)]
659mod tests {
660    use super::*;
661    use crate::cep95::utils::*;
662    use odra::{
663        host::{Deployer, HostEnv, NoArgs},
664        prelude::Addressable,
665        VmError
666    };
667    use odra_test;
668
669    fn setup() -> (HostEnv, BasicCep95HostRef) {
670        let env = odra_test::env();
671        let cep95 = BasicCep95::try_deploy(
672            &env,
673            BasicCep95InitArgs {
674                name: "TestToken".to_string(),
675                symbol: "TT".to_string()
676            }
677        )
678        .unwrap();
679        (env, cep95)
680    }
681
682    #[test]
683    fn test_deploy() {
684        let env = odra_test::env();
685        let cep95 = BasicCep95::try_deploy(
686            &env,
687            BasicCep95InitArgs {
688                name: "TestToken".to_string(),
689                symbol: "TT".to_string()
690            }
691        );
692        assert!(cep95.is_ok());
693    }
694
695    #[test]
696    fn test_name() {
697        let (env, cep95) = setup();
698        let name = cep95.name();
699        assert_eq!(name, "TestToken");
700    }
701
702    #[test]
703    fn test_symbol() {
704        let (env, cep95) = setup();
705        let symbol = cep95.symbol();
706        assert_eq!(symbol, "TT");
707    }
708
709    #[test]
710    fn test_mint() {
711        let (env, mut cep95) = setup();
712        let owner = env.caller();
713
714        let token_id = U256::from(1);
715        let metadata = vec![("key".to_string(), "value".to_string())];
716        cep95.mint(owner, token_id, metadata);
717
718        assert_eq!(cep95.balance_of(owner), U256::from(1));
719        assert_eq!(cep95.owner_of(token_id), Some(owner));
720        assert_eq!(
721            cep95.token_metadata(token_id),
722            vec![("key".to_string(), "value".to_string())]
723        );
724        assert!(env.emitted(&cep95, "Mint"));
725    }
726
727    #[test]
728    fn test_minting_existing_token() {
729        let (env, mut cep95) = setup();
730        let owner = env.caller();
731
732        let token_id = U256::from(1);
733        let metadata = vec![("key".to_string(), "value".to_string())];
734        cep95.mint(owner, token_id, metadata.clone());
735
736        let result = cep95.try_mint(owner, token_id, metadata);
737        assert_eq!(result, Err(Error::TokenAlreadyExists.into()));
738    }
739
740    #[test]
741    fn test_mint_many_tokens() {
742        let (env, mut cep95) = setup();
743        let owner = env.caller();
744
745        let token_id1 = U256::from(1);
746        let metadata1 = vec![("key1".to_string(), "value1".to_string())];
747        cep95.mint(owner, token_id1, metadata1);
748
749        let token_id2 = U256::from(2);
750        let metadata2 = vec![("key2".to_string(), "value2".to_string())];
751        cep95.mint(owner, token_id2, metadata2);
752
753        assert_eq!(cep95.balance_of(owner), U256::from(2));
754        assert_eq!(cep95.owner_of(token_id1), Some(owner));
755        assert_eq!(cep95.owner_of(token_id2), Some(owner));
756    }
757
758    #[test]
759    fn test_burn() {
760        let (env, mut cep95) = setup();
761        let owner = env.caller();
762
763        let token_id = U256::from(1);
764        let metadata = vec![("key".to_string(), "value".to_string())];
765        cep95.mint(owner, token_id, metadata);
766        cep95.burn(token_id);
767
768        assert_eq!(cep95.balance_of(owner), U256::from(0));
769        assert_eq!(cep95.owner_of(token_id), None);
770        assert_eq!(
771            cep95.try_token_metadata(token_id),
772            Err(Error::InvalidTokenId.into())
773        );
774        assert!(env.emitted(&cep95, "Burn"));
775    }
776
777    #[test]
778    fn test_burn_non_existing_token() {
779        let (env, mut cep95) = setup();
780        let owner = env.caller();
781
782        let token_id = U256::from(1);
783        let result = cep95.try_burn(token_id);
784        assert_eq!(result, Err(Error::InvalidTokenId.into()));
785    }
786
787    #[test]
788    fn test_safe_transfer_to_receiver() {
789        let (env, mut cep95) = setup();
790        let nft_receiver = NFTReceiver::deploy(&env, NoArgs);
791        let recipient = nft_receiver.address();
792        let owner = env.caller();
793
794        let token_id = U256::from(1);
795        let metadata = vec![("key".to_string(), "value".to_string())];
796        cep95.mint(owner, token_id, metadata);
797
798        cep95.safe_transfer_from(owner, recipient, token_id, None);
799
800        assert_eq!(cep95.balance_of(owner), U256::from(0));
801        assert_eq!(cep95.balance_of(recipient), U256::from(1));
802        assert_eq!(
803            nft_receiver.last_call_result(),
804            ((owner, owner), (token_id, None))
805        );
806    }
807
808    #[test]
809    fn test_safe_transfer_to_non_receiver() {
810        let (env, mut cep95) = setup();
811        let contract = BasicContract::deploy(&env, NoArgs);
812        let recipient = contract.address();
813        let owner = env.caller();
814
815        let token_id = U256::from(1);
816        let metadata = vec![("key".to_string(), "value".to_string())];
817        cep95.mint(owner, token_id, metadata);
818
819        let result = cep95.try_safe_transfer_from(owner, recipient, token_id, None);
820        assert_eq!(
821            result,
822            Err(OdraError::VmError(VmError::NoSuchMethod(
823                "on_cep95_received".to_string()
824            )))
825        );
826
827        assert_eq!(cep95.balance_of(owner), U256::from(1));
828        assert_eq!(cep95.balance_of(recipient), U256::from(0));
829    }
830
831    #[test]
832    fn test_safe_transfer_to_rejecting_receiver() {
833        let (env, mut cep95) = setup();
834
835        let owner = env.get_account(0);
836        let contract = RejectingNFTReceiver::deploy(&env, NoArgs);
837        let recipient = contract.address();
838
839        let token_id = U256::from(1);
840        let metadata = vec![("key".to_string(), "value".to_string())];
841        cep95.mint(owner, token_id, metadata);
842
843        let result = cep95.try_safe_transfer_from(owner, recipient, token_id, None);
844        assert_eq!(result, Err(Error::TransferFailed.into()));
845
846        assert_eq!(cep95.balance_of(owner), U256::from(1));
847        assert_eq!(cep95.balance_of(recipient), U256::from(0));
848    }
849
850    #[test]
851    fn test_transfer() {
852        let (env, mut cep95) = setup();
853
854        let owner = env.get_account(0);
855        let recipient = env.get_account(10);
856
857        let token_id = U256::from(1);
858        let metadata = vec![("key".to_string(), "value".to_string())];
859        cep95.mint(owner, token_id, metadata);
860        cep95.transfer_from(owner, recipient, token_id);
861
862        assert_eq!(cep95.balance_of(owner), U256::from(0));
863        assert_eq!(cep95.balance_of(recipient), U256::from(1));
864        assert!(env.emitted(&cep95, "Transfer"));
865    }
866
867    #[test]
868    fn test_transfer_non_existing_token() {
869        let (env, mut cep95) = setup();
870
871        let owner = env.get_account(0);
872        let recipient = env.get_account(10);
873
874        let token_id = U256::from(1);
875        let metadata = vec![("key".to_string(), "value".to_string())];
876        cep95.mint(owner, token_id, metadata);
877
878        let result = cep95.try_transfer_from(owner, recipient, U256::from(2));
879        assert_eq!(result, Err(Error::InvalidTokenId.into()));
880    }
881
882    #[test]
883    fn test_transfer_from_non_owner() {
884        let (env, mut cep95) = setup();
885
886        let recipient = env.get_account(10);
887        let non_owner = env.get_account(11);
888        let owner = env.get_account(0);
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
894        let result = cep95.try_transfer_from(non_owner, recipient, token_id);
895        assert_eq!(result, Err(Error::NotAnOwnerOrApproved.into()));
896        assert_eq!(cep95.balance_of(owner), U256::from(1));
897        assert_eq!(cep95.balance_of(recipient), U256::from(0));
898    }
899
900    #[test]
901    fn test_approve() {
902        let (env, mut cep95) = setup();
903        let owner = env.caller();
904
905        let token_id = U256::from(1);
906        let metadata = vec![("key".to_string(), "value".to_string())];
907        cep95.mint(owner, token_id, metadata);
908
909        let spender = env.get_account(10);
910        cep95.approve(spender, token_id);
911
912        assert_eq!(cep95.approved_for(token_id), Some(spender));
913        assert!(env.emitted_event(
914            &cep95,
915            Approval {
916                owner,
917                spender,
918                token_id
919            }
920        ));
921    }
922
923    #[test]
924    fn test_approve_by_non_owner() {
925        let (env, mut cep95) = setup();
926        let owner = env.get_account(0);
927        let non_owner = env.get_account(11);
928
929        let token_id = U256::from(1);
930        let metadata = vec![("key".to_string(), "value".to_string())];
931        cep95.mint(owner, token_id, metadata);
932
933        let spender = env.get_account(10);
934        env.set_caller(non_owner);
935        let result = cep95.try_approve(spender, token_id);
936        assert_eq!(result, Err(Error::NotAnOwnerOrApproved.into()));
937    }
938
939    #[test]
940    fn test_transfer_by_approved() {
941        let (env, mut cep95) = setup();
942        let owner = env.get_account(0);
943        let recipient = env.get_account(10);
944
945        let token_id = U256::from(1);
946        let metadata = vec![("key".to_string(), "value".to_string())];
947        cep95.mint(owner, token_id, metadata);
948
949        let spender = env.get_account(11);
950        cep95.approve(spender, token_id);
951        env.set_caller(spender);
952        cep95.transfer_from(owner, recipient, token_id);
953
954        assert_eq!(cep95.balance_of(owner), U256::from(0));
955        assert_eq!(cep95.balance_of(recipient), U256::from(1));
956        assert!(env.emitted_event(
957            &cep95,
958            Transfer {
959                from: owner,
960                to: recipient,
961                token_id
962            }
963        ));
964    }
965
966    #[test]
967    fn test_approve_for_all() {
968        let (env, mut cep95) = setup();
969        let owner = env.caller();
970        let operator = env.get_account(10);
971
972        cep95.approve_for_all(operator);
973
974        assert!(cep95.is_approved_for_all(owner, operator));
975        assert!(env.emitted(&cep95, "ApprovalForAll"));
976    }
977
978    #[test]
979    fn test_revoke_approval_for_all() {
980        let (env, mut cep95) = setup();
981        let owner = env.caller();
982        let operator = env.get_account(10);
983
984        cep95.approve_for_all(operator);
985        cep95.revoke_approval_for_all(operator);
986
987        assert!(!cep95.is_approved_for_all(owner, operator));
988        assert!(env.emitted(&cep95, "RevokeApprovalForAll"));
989    }
990
991    #[test]
992    fn test_approve_for_all_self() {
993        let (env, mut cep95) = setup();
994        let owner = env.caller();
995
996        let result = cep95.try_approve_for_all(owner);
997        assert_eq!(result, Err(Error::ApproveToCaller.into()));
998    }
999
1000    #[test]
1001    fn test_revoke_approval() {
1002        let (env, mut cep95) = setup();
1003        let owner = env.caller();
1004
1005        let token_id = U256::from(1);
1006        let metadata = vec![("key".to_string(), "value".to_string())];
1007        cep95.mint(owner, token_id, metadata);
1008
1009        let spender = env.get_account(10);
1010        cep95.approve(spender, token_id);
1011        cep95.revoke_approval(token_id);
1012
1013        assert_eq!(cep95.approved_for(token_id), None);
1014        assert!(env.emitted(&cep95, "RevokeApproval"));
1015    }
1016
1017    #[test]
1018    fn revoke_non_existing_approval() {
1019        let (env, mut cep95) = setup();
1020        let owner = env.caller();
1021
1022        let token_id = U256::from(1);
1023        let metadata = vec![("key".to_string(), "value".to_string())];
1024        cep95.mint(owner, token_id, metadata);
1025
1026        let result = cep95.try_revoke_approval(token_id);
1027        assert_eq!(result, Err(Error::ValueNotSet.into()));
1028    }
1029
1030    #[test]
1031    fn test_revoke_approval_by_non_owner() {
1032        let (env, mut cep95) = setup();
1033        let owner = env.get_account(0);
1034        let non_owner = env.get_account(11);
1035
1036        let token_id = U256::from(1);
1037        let metadata = vec![("key".to_string(), "value".to_string())];
1038        cep95.mint(owner, token_id, metadata);
1039
1040        let spender = env.get_account(10);
1041        cep95.approve(spender, token_id);
1042        env.set_caller(non_owner);
1043        let result = cep95.try_revoke_approval(token_id);
1044        assert_eq!(result, Err(Error::NotAnOwnerOrApproved.into()));
1045    }
1046
1047    #[test]
1048    fn test_metadata() {
1049        let (env, mut cep95) = setup();
1050        let owner = env.caller();
1051
1052        let token_id = U256::from(1);
1053        let metadata = vec![("key".to_string(), "value".to_string())];
1054        cep95.mint(owner, token_id, metadata);
1055
1056        assert_eq!(
1057            cep95.token_metadata(token_id),
1058            vec![("key".to_string(), "value".to_string())]
1059        );
1060    }
1061
1062    #[test]
1063    fn test_transfer_by_operator() {
1064        let (env, mut cep95) = setup();
1065        let owner = env.caller();
1066        let recipient = env.get_account(10);
1067        let operator = env.get_account(11);
1068
1069        let token_id1 = U256::from(1);
1070        let metadata = vec![("key".to_string(), "value".to_string())];
1071        cep95.mint(owner, token_id1, metadata.clone());
1072
1073        cep95.approve_for_all(operator);
1074        let token_id2 = U256::from(2);
1075        cep95.mint(owner, token_id2, metadata);
1076
1077        env.set_caller(operator);
1078        cep95.transfer_from(owner, recipient, token_id1);
1079        cep95.transfer_from(owner, recipient, token_id2);
1080
1081        assert_eq!(cep95.balance_of(owner), U256::from(0));
1082        assert_eq!(cep95.balance_of(recipient), U256::from(2));
1083    }
1084
1085    #[test]
1086    fn test_update_metadata() {
1087        let (env, mut cep95) = setup();
1088        let owner = env.caller();
1089
1090        let token_id = U256::from(1);
1091        let metadata = vec![
1092            ("age".to_string(), "30".to_string()),
1093            ("name".to_string(), "Alice".to_string()),
1094        ];
1095        cep95.mint(owner, token_id, metadata);
1096
1097        let new_metadata = vec![("name".to_string(), "Bob".to_string())];
1098        cep95.update_metadata(token_id, new_metadata);
1099
1100        assert_eq!(
1101            cep95.token_metadata(token_id),
1102            vec![
1103                ("age".to_string(), "30".to_string()),
1104                ("name".to_string(), "Bob".to_string()),
1105            ]
1106        );
1107        assert!(env.emitted(&cep95, "MetadataUpdate"));
1108    }
1109
1110    #[test]
1111    fn test_set_metadata() {
1112        let (env, mut cep95) = setup();
1113        let owner = env.caller();
1114
1115        let token_id = U256::from(1);
1116        let metadata = vec![
1117            ("age".to_string(), "30".to_string()),
1118            ("name".to_string(), "Alice".to_string()),
1119        ];
1120        cep95.mint(owner, token_id, metadata);
1121
1122        let new_metadata = vec![("name".to_string(), "Bob".to_string())];
1123        cep95.set_metadata(token_id, new_metadata);
1124
1125        assert_eq!(
1126            cep95.token_metadata(token_id),
1127            vec![("name".to_string(), "Bob".to_string())]
1128        );
1129        assert!(env.emitted(&cep95, "MetadataUpdate"));
1130    }
1131}