1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//! Defines the required context traits for ICS-721 to interact with host
//! machine.
use ibc_core::host::types::identifiers::{ChannelId, PortId};
use ibc_core::primitives::prelude::*;
use ibc_core::primitives::Signer;

use crate::types::error::NftTransferError;
use crate::types::{
    ClassData, ClassId, ClassUri, Memo, PrefixedClassId, TokenData, TokenId, TokenUri,
};

pub trait NftContext {
    /// Get the class ID of the token
    fn get_class_id(&self) -> &ClassId;

    /// Get the token ID
    fn get_id(&self) -> &TokenId;

    /// Get the token URI
    fn get_uri(&self) -> Option<&TokenUri>;

    /// Get the token Data
    fn get_data(&self) -> Option<&TokenData>;
}

pub trait NftClassContext {
    /// Get the class ID
    fn get_id(&self) -> &ClassId;

    /// Get the class URI
    fn get_uri(&self) -> Option<&ClassUri>;

    /// Get the class Data
    fn get_data(&self) -> Option<&ClassData>;
}

/// Read-only methods required in NFT transfer validation context.
pub trait NftTransferValidationContext {
    type AccountId: TryFrom<Signer> + PartialEq;
    type Nft: NftContext;
    type NftClass: NftClassContext;

    /// get_port returns the portID for the transfer module.
    fn get_port(&self) -> Result<PortId, NftTransferError>;

    /// Returns Ok() if the host chain supports sending NFTs.
    fn can_send_nft(&self) -> Result<(), NftTransferError>;

    /// Returns Ok() if the host chain supports receiving NFTs.
    fn can_receive_nft(&self) -> Result<(), NftTransferError>;

    /// Validates that the NFT can be created or updated successfully.
    ///
    /// Note: some existing ICS-721 implementations may not strictly adhere to
    /// the ICS-721 class data structure. The
    /// [`ClassData`] associated with this
    /// implementation can take any valid JSON format. If your project requires
    /// ICS-721 format for the `ClassData`, ensure correctness by checking with
    /// [`parse_as_ics721_data()`](crate::types::Data::parse_as_ics721_data).
    fn create_or_update_class_validate(
        &self,
        class_id: &PrefixedClassId,
        class_uri: Option<&ClassUri>,
        class_data: Option<&ClassData>,
    ) -> Result<(), NftTransferError>;

    /// Validates that the tokens can be escrowed successfully.
    ///
    /// The owner of the NFT should be checked in this validation.
    /// `memo` field allows to incorporate additional contextual details in the
    /// escrow validation.
    fn escrow_nft_validate(
        &self,
        from_account: &Self::AccountId,
        port_id: &PortId,
        channel_id: &ChannelId,
        class_id: &PrefixedClassId,
        token_id: &TokenId,
        memo: &Memo,
    ) -> Result<(), NftTransferError>;

    /// Validates that the NFT can be unescrowed successfully.
    fn unescrow_nft_validate(
        &self,
        to_account: &Self::AccountId,
        port_id: &PortId,
        channel_id: &ChannelId,
        class_id: &PrefixedClassId,
        token_id: &TokenId,
    ) -> Result<(), NftTransferError>;

    /// Validates the receiver account and the NFT input
    ///
    /// Note: some existing ICS-721 implementations may not strictly adhere to
    /// the ICS-721 token data structure. The
    /// [`TokenData`] associated with this
    /// implementation can take any valid JSON format. If your project requires
    /// ICS-721 format for `TokenData`, ensure correctness by checking with
    /// [`parse_as_ics721_data()`](crate::types::Data::parse_as_ics721_data).
    fn mint_nft_validate(
        &self,
        account: &Self::AccountId,
        class_id: &PrefixedClassId,
        token_id: &TokenId,
        token_uri: Option<&TokenUri>,
        token_data: Option<&TokenData>,
    ) -> Result<(), NftTransferError>;

    /// Validates the sender account and the coin input before burning.
    ///
    /// The owner of the NFT should be checked in this validation.
    /// `memo` field allows to incorporate additional contextual details in the
    /// burn validation.
    fn burn_nft_validate(
        &self,
        account: &Self::AccountId,
        class_id: &PrefixedClassId,
        token_id: &TokenId,
        memo: &Memo,
    ) -> Result<(), NftTransferError>;

    /// Returns a hash of the prefixed class ID and the token ID.
    /// Implement only if the host chain supports hashed class ID and token ID.
    fn token_hash_string(
        &self,
        _class_id: &PrefixedClassId,
        _token_id: &TokenId,
    ) -> Option<String> {
        None
    }

    /// Returns the NFT
    fn get_nft(
        &self,
        class_id: &PrefixedClassId,
        token_id: &TokenId,
    ) -> Result<Self::Nft, NftTransferError>;

    /// Returns the NFT class
    fn get_nft_class(&self, class_id: &PrefixedClassId)
        -> Result<Self::NftClass, NftTransferError>;
}

/// Read-write methods required in NFT transfer execution context.
pub trait NftTransferExecutionContext: NftTransferValidationContext {
    /// Creates a new NFT Class identified by classId. If the class ID already exists, it updates the class metadata.
    fn create_or_update_class_execute(
        &self,
        class_id: &PrefixedClassId,
        class_uri: Option<&ClassUri>,
        class_data: Option<&ClassData>,
    ) -> Result<(), NftTransferError>;

    /// Executes the escrow of the NFT in a user account.
    ///
    /// `memo` field allows to incorporate additional contextual details in the
    /// escrow execution.
    fn escrow_nft_execute(
        &mut self,
        from_account: &Self::AccountId,
        port_id: &PortId,
        channel_id: &ChannelId,
        class_id: &PrefixedClassId,
        token_id: &TokenId,
        memo: &Memo,
    ) -> Result<(), NftTransferError>;

    /// Executes the unescrow of the NFT in a user account.
    fn unescrow_nft_execute(
        &mut self,
        to_account: &Self::AccountId,
        port_id: &PortId,
        channel_id: &ChannelId,
        class_id: &PrefixedClassId,
        token_id: &TokenId,
    ) -> Result<(), NftTransferError>;

    /// Executes minting of the NFT in a user account.
    fn mint_nft_execute(
        &mut self,
        account: &Self::AccountId,
        class_id: &PrefixedClassId,
        token_id: &TokenId,
        token_uri: Option<&TokenUri>,
        token_data: Option<&TokenData>,
    ) -> Result<(), NftTransferError>;

    /// Executes burning of the NFT in a user account.
    ///
    /// `memo` field allows to incorporate additional contextual details in the
    /// burn execution.
    fn burn_nft_execute(
        &mut self,
        account: &Self::AccountId,
        class_id: &PrefixedClassId,
        token_id: &TokenId,
        memo: &Memo,
    ) -> Result<(), NftTransferError>;
}