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