Skip to main content

odra_modules/
cep95.rs

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