sg_name/
lib.rs

1use cosmwasm_schema::{cw_serde, QueryResponses};
2use cosmwasm_std::{to_vec, Addr};
3
4pub const MAX_TEXT_LENGTH: u32 = 512;
5
6pub type TokenId = String;
7
8#[cw_serde]
9pub struct NFT {
10    pub collection: Addr,
11    pub token_id: TokenId,
12}
13
14impl NFT {
15    pub fn into_json_string(self: &NFT) -> String {
16        String::from_utf8(to_vec(&self).unwrap_or_default()).unwrap_or_default()
17    }
18}
19
20#[cw_serde]
21pub struct TextRecord {
22    pub name: String,           // "twitter"
23    pub value: String,          // "shan3v"
24    pub verified: Option<bool>, // can only be set by oracle
25}
26
27impl TextRecord {
28    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
29        Self {
30            name: name.into(),
31            value: value.into(),
32            verified: None,
33        }
34    }
35
36    pub fn into_json_string(self: &TextRecord) -> String {
37        String::from_utf8(to_vec(&self).unwrap_or_default()).unwrap_or_default()
38    }
39}
40
41/// Note that the address mapped to the name is stored in `token_uri`.
42#[cw_serde]
43#[derive(Default)]
44pub struct Metadata {
45    pub image_nft: Option<NFT>,
46    pub records: Vec<TextRecord>,
47}
48
49impl Metadata {
50    // Yes this is rather ugly. It is used to convert metadata into a JSON
51    // string for use in emitting events. Events can only be strings, so
52    // serializing it into a JSON string allows the indexer to parse it
53    // and represent it as a type. Note that we have to use the CosmWasm fork
54    // of serde_json to avoid floats.
55    pub fn into_json_string(self: &Metadata) -> String {
56        String::from_utf8(to_vec(&self).unwrap_or_default()).unwrap_or_default()
57    }
58}
59
60#[cw_serde]
61pub enum SgNameExecuteMsg {
62    /// Set name marketplace contract address
63    SetNameMarketplace { address: String },
64    /// Set an address for name reverse lookup
65    /// Can be an EOA or a contract address
66    AssociateAddress {
67        name: String,
68        address: Option<String>,
69    },
70    /// Update image
71    UpdateImageNft { name: String, nft: Option<NFT> },
72    /// Update Metadata
73    UpdateMetadata {
74        name: String,
75        metadata: Option<Metadata>,
76    },
77    /// Add text record ex: twitter handle, discord name, etc
78    AddTextRecord { name: String, record: TextRecord },
79    /// Remove text record ex: twitter handle, discord name, etc
80    RemoveTextRecord { name: String, record_name: String },
81    /// Update text record ex: twitter handle, discord name, etc
82    UpdateTextRecord { name: String, record: TextRecord },
83    /// Verify a text record (via oracle)
84    VerifyTextRecord {
85        name: String,
86        record_name: String,
87        result: bool,
88    },
89    /// Update the reset the verification oracle
90    UpdateVerifier { verifier: Option<String> },
91}
92
93#[cw_serde]
94#[derive(QueryResponses)]
95pub enum SgNameQueryMsg {
96    /// `address` can be any Bech32 encoded address. It will be
97    /// converted to a stars address for internal mapping.
98    #[returns(String)]
99    Name { address: String },
100    #[returns(Addr)]
101    NameMarketplace {},
102    #[returns(String)]
103    AssociatedAddress { name: String },
104    #[returns(Option<NFT>)]
105    ImageNFT { name: String },
106    #[returns(Vec<TextRecord>)]
107    TextRecords { name: String },
108    #[returns(bool)]
109    IsTwitterVerified { name: String },
110}
111
112#[cfg(test)]
113mod test {
114    use super::*;
115
116    #[test]
117    fn encode_nft() {
118        let nft = NFT {
119            collection: Addr::unchecked("stars1y54exmx84cqtasvjnskf9f63djuuj68p2th570"),
120            token_id: "1".to_string(),
121        };
122        let json = nft.into_json_string();
123        assert_eq!(
124            json,
125            r#"{"collection":"stars1y54exmx84cqtasvjnskf9f63djuuj68p2th570","token_id":"1"}"#
126        );
127    }
128
129    #[test]
130    fn encode_text_record() {
131        let mut record = TextRecord::new("twitter", "shan3v");
132        let json = record.into_json_string();
133        assert_eq!(
134            json,
135            r#"{"name":"twitter","value":"shan3v","verified":null}"#
136        );
137
138        record.verified = Some(true);
139
140        let json = record.into_json_string();
141        assert_eq!(
142            json,
143            r#"{"name":"twitter","value":"shan3v","verified":true}"#
144        );
145
146        record.verified = Some(false);
147
148        let json = record.into_json_string();
149        assert_eq!(
150            json,
151            r#"{"name":"twitter","value":"shan3v","verified":false}"#
152        );
153    }
154
155    #[test]
156    fn encode_metadata() {
157        let image_nft = Some(NFT {
158            collection: Addr::unchecked("stars1y54exmx84cqtasvjnskf9f63djuuj68p2th570"),
159            token_id: "1".to_string(),
160        });
161        let record_1 = TextRecord::new("twitter", "shan3v");
162        let mut record_2 = TextRecord::new("discord", "shan3v");
163        record_2.verified = Some(true);
164        let records = vec![record_1, record_2];
165        let metadata = Metadata { image_nft, records };
166
167        let json = metadata.into_json_string();
168        assert_eq!(
169            json,
170            r#"{"image_nft":{"collection":"stars1y54exmx84cqtasvjnskf9f63djuuj68p2th570","token_id":"1"},"records":[{"name":"twitter","value":"shan3v","verified":null},{"name":"discord","value":"shan3v","verified":true}]}"#,
171        );
172    }
173}