nft_server/
open_sea.rs

1use ethers::types::Address;
2use serde::{Deserialize, Serialize};
3use url::Url;
4
5use crate::NftImage;
6
7/// OpenSea display types for [`OpenSeaAttributeValue`] types.
8///
9/// See [OpenSea documentation](https://docs.opensea.io/docs/metadata-standards)
10/// for examples
11#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13pub enum OpenSeaDisplayType {
14    /// Date
15    Date,
16    /// Number
17    Number,
18    /// BoostPercentage
19    BoostPercentage,
20    /// BoostNumber
21    BoostNumber,
22}
23
24/// OpenSea attribute value types.
25///
26/// See [OpenSea documentation](https://docs.opensea.io/docs/metadata-standards)
27/// for examples
28#[derive(Clone, Debug, Serialize, Deserialize)]
29#[serde(untagged)]
30pub enum OpenSeaAttributeValue {
31    /// A `String` attribute value
32    String {
33        /// The value
34        value: String,
35    },
36    /// An `Integer` with display type and max value
37    Integer {
38        /// The value
39        value: i64,
40        /// The display type on OpenSea
41        #[serde(default, skip_serializing_if = "Option::is_none")]
42        display_type: Option<OpenSeaDisplayType>,
43        #[serde(default, skip_serializing_if = "Option::is_none")]
44        /// The maximum value for display on OpenSea
45        max_value: Option<i64>,
46    },
47    /// A `Float` with display type and max value
48    Float {
49        /// The value
50        value: f64,
51        #[serde(default, skip_serializing_if = "Option::is_none")]
52        /// The display type on OpenSea
53        display_type: Option<OpenSeaDisplayType>,
54        #[serde(default, skip_serializing_if = "Option::is_none")]
55        /// The maximum value for display on OpenSea
56        max_value: Option<f64>,
57    },
58}
59
60impl<T> From<T> for OpenSeaAttributeValue
61where
62    T: AsRef<str>,
63{
64    fn from(t: T) -> Self {
65        OpenSeaAttributeValue::String {
66            value: t.as_ref().to_string(),
67        }
68    }
69}
70
71/// An OpenSea-style NFT attribute. Optionally includes an attribute name (the
72/// `trait_type`), as well as the [`OpenSeaAttributeValue`]/
73#[derive(Clone, Debug, Serialize, Deserialize)]
74pub struct OpenSeaAttribute {
75    /// The attribute name (if any)
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub trait_type: Option<String>,
78    /// The attribute value
79    #[serde(flatten)]
80    pub value: OpenSeaAttributeValue,
81}
82
83impl OpenSeaAttribute {
84    /// Shortcut to instantiate a string attribute
85    pub fn string<S, T>(trait_type: Option<S>, value: T) -> Self
86    where
87        S: AsRef<str>,
88        T: AsRef<str>,
89    {
90        Self {
91            trait_type: trait_type.map(|s| s.as_ref().to_string()),
92            value: value.into(),
93        }
94    }
95
96    /// Shortcut to instantiate a float attribute
97    pub fn float<S>(trait_type: Option<S>, value: f64, max_value: Option<f64>) -> Self
98    where
99        S: AsRef<str>,
100    {
101        Self {
102            trait_type: trait_type.map(|s| s.as_ref().to_string()),
103            value: OpenSeaAttributeValue::Float {
104                value,
105                display_type: None,
106                max_value,
107            },
108        }
109    }
110
111    /// Shortcut to instantiate an integer attribute
112    pub fn integer<S>(trait_type: Option<S>, value: i64, max_value: Option<i64>) -> Self
113    where
114        S: AsRef<str>,
115    {
116        Self {
117            trait_type: trait_type.map(|s| s.as_ref().to_string()),
118            value: OpenSeaAttributeValue::Integer {
119                value,
120                display_type: None,
121                max_value,
122            },
123        }
124    }
125}
126
127/// OpenSea-style contract-level metadata
128#[derive(Clone, Debug, Serialize, Deserialize)]
129pub struct ContractMetadata {
130    /// The collection name
131    pub name: String,
132    /// The collection description
133    pub description: String,
134    /// The collection image
135    #[serde(flatten)]
136    pub image: NftImage,
137    /// An external link for the NFT collection
138    pub external_link: Url,
139    /// The seller fee, in bps
140    pub seller_fee_basis_points: usize,
141    /// The recipient of the seller fee
142    pub fee_recipient: Address,
143}