devhub_shared/proposal/
mod.rs

1pub mod timeline;
2
3use std::collections::HashSet;
4
5use self::timeline::{TimelineStatusV1, VersionedTimelineStatus};
6
7use crate::rfp::RFPId;
8use crate::str_serializers::*;
9
10use near_sdk::{near, AccountId, BlockHeight, Timestamp};
11
12pub type ProposalId = u32;
13
14type PostTag = String;
15
16#[near(serializers=[borsh, json])]
17#[derive(Clone, Debug)]
18#[serde(tag = "proposal_version")]
19pub enum VersionedProposal {
20    V0(Proposal),
21}
22
23#[near(serializers=[borsh, json])]
24#[derive(Clone, Debug)]
25pub struct Proposal {
26    pub id: ProposalId,
27    pub author_id: AccountId,
28    #[serde(
29        serialize_with = "u64_dec_format::serialize",
30        deserialize_with = "u64_dec_format::deserialize"
31    )]
32    pub social_db_post_block_height: BlockHeight,
33    pub snapshot: ProposalSnapshot,
34    // // Excludes the current snapshot itself.
35    pub snapshot_history: Vec<ProposalSnapshot>,
36}
37
38impl From<VersionedProposal> for Proposal {
39    fn from(vp: VersionedProposal) -> Self {
40        match vp {
41            VersionedProposal::V0(v0) => v0,
42        }
43    }
44}
45
46impl From<Proposal> for VersionedProposal {
47    fn from(p: Proposal) -> Self {
48        VersionedProposal::V0(p)
49    }
50}
51
52#[near(serializers=[borsh, json])]
53#[derive(Clone, Debug)]
54pub struct ProposalSnapshot {
55    pub editor_id: AccountId,
56    #[serde(
57        serialize_with = "u64_dec_format::serialize",
58        deserialize_with = "u64_dec_format::deserialize"
59    )]
60    pub timestamp: Timestamp,
61    pub labels: HashSet<PostTag>,
62    #[serde(flatten)]
63    pub body: VersionedProposalBody,
64}
65
66#[near(serializers=[borsh, json])]
67#[derive(Clone, Debug)]
68pub struct ProposalBodyV0 {
69    pub name: String,
70    pub category: String,
71    pub summary: String,
72    pub description: String,
73    pub linked_proposals: Vec<ProposalId>,
74    #[serde(
75        serialize_with = "u32_dec_format::serialize",
76        deserialize_with = "u32_dec_format::deserialize"
77    )]
78    pub requested_sponsorship_usd_amount: u32,
79    pub requested_sponsorship_paid_in_currency: ProposalFundingCurrency,
80    pub receiver_account: AccountId,
81    pub requested_sponsor: AccountId,
82    pub supervisor: Option<AccountId>,
83    pub timeline: TimelineStatusV1,
84}
85
86#[near(serializers=[borsh, json])]
87#[derive(Clone, Debug)]
88pub struct ProposalBodyV1 {
89    pub name: String,
90    pub category: String,
91    pub summary: String,
92    pub description: String,
93    pub linked_proposals: Vec<ProposalId>,
94    #[serde(
95        serialize_with = "u32_dec_format::serialize",
96        deserialize_with = "u32_dec_format::deserialize"
97    )]
98    pub requested_sponsorship_usd_amount: u32,
99    pub requested_sponsorship_paid_in_currency: ProposalFundingCurrency,
100    pub receiver_account: AccountId,
101    pub requested_sponsor: AccountId,
102    pub supervisor: Option<AccountId>,
103    pub timeline: TimelineStatusV1,
104    pub linked_rfp: Option<RFPId>,
105}
106
107#[near(serializers=[borsh, json])]
108#[derive(Clone, Debug)]
109pub struct ProposalBodyV2 {
110    pub name: String,
111    pub category: String,
112    pub summary: String,
113    pub description: String,
114    pub linked_proposals: Vec<ProposalId>,
115    #[serde(
116        serialize_with = "u32_dec_format::serialize",
117        deserialize_with = "u32_dec_format::deserialize"
118    )]
119    pub requested_sponsorship_usd_amount: u32,
120    pub requested_sponsorship_paid_in_currency: ProposalFundingCurrency,
121    pub receiver_account: AccountId,
122    pub requested_sponsor: AccountId,
123    pub supervisor: Option<AccountId>,
124    pub timeline: VersionedTimelineStatus,
125    pub linked_rfp: Option<RFPId>,
126}
127
128#[near(serializers=[borsh, json])]
129#[derive(Clone, Debug)]
130#[serde(tag = "proposal_body_version")]
131pub enum VersionedProposalBody {
132    V0(ProposalBodyV0),
133    V1(ProposalBodyV1),
134    V2(ProposalBodyV2),
135}
136
137impl From<ProposalBodyV0> for ProposalBodyV1 {
138    fn from(v0: ProposalBodyV0) -> Self {
139        ProposalBodyV1 {
140            name: v0.name,
141            category: v0.category,
142            summary: v0.summary,
143            description: v0.description,
144            linked_proposals: v0.linked_proposals,
145            requested_sponsorship_usd_amount: v0.requested_sponsorship_usd_amount,
146            requested_sponsorship_paid_in_currency: v0.requested_sponsorship_paid_in_currency,
147            receiver_account: v0.receiver_account,
148            requested_sponsor: v0.requested_sponsor,
149            supervisor: v0.supervisor,
150            timeline: v0.timeline,
151            linked_rfp: None,
152        }
153    }
154}
155
156impl From<ProposalBodyV1> for ProposalBodyV2 {
157    fn from(v1: ProposalBodyV1) -> Self {
158        ProposalBodyV2 {
159            name: v1.name,
160            category: v1.category,
161            summary: v1.summary,
162            description: v1.description,
163            linked_proposals: v1.linked_proposals,
164            requested_sponsorship_usd_amount: v1.requested_sponsorship_usd_amount,
165            requested_sponsorship_paid_in_currency: v1.requested_sponsorship_paid_in_currency,
166            receiver_account: v1.receiver_account,
167            requested_sponsor: v1.requested_sponsor,
168            supervisor: v1.supervisor,
169            timeline: v1.timeline.into(),
170            linked_rfp: v1.linked_rfp,
171        }
172    }
173}
174
175impl From<VersionedProposalBody> for ProposalBodyV0 {
176    fn from(solution: VersionedProposalBody) -> Self {
177        match solution {
178            VersionedProposalBody::V0(v0) => v0,
179            _ => unimplemented!(),
180        }
181    }
182}
183
184impl From<VersionedProposalBody> for ProposalBodyV1 {
185    fn from(solution: VersionedProposalBody) -> Self {
186        match solution {
187            VersionedProposalBody::V0(v0) => v0.into(),
188            VersionedProposalBody::V1(v1) => v1,
189            _ => unimplemented!(),
190        }
191    }
192}
193
194impl From<VersionedProposalBody> for ProposalBodyV2 {
195    fn from(solution: VersionedProposalBody) -> Self {
196        match solution {
197            VersionedProposalBody::V0(v0) => {
198                let v1: ProposalBodyV1 = v0.into();
199                v1.into()
200            }
201            VersionedProposalBody::V1(v1) => v1.into(),
202            VersionedProposalBody::V2(v2) => v2,
203        }
204    }
205}
206
207impl From<ProposalBodyV0> for VersionedProposalBody {
208    fn from(p: ProposalBodyV0) -> Self {
209        VersionedProposalBody::V0(p)
210    }
211}
212
213impl From<ProposalBodyV1> for VersionedProposalBody {
214    fn from(p: ProposalBodyV1) -> Self {
215        VersionedProposalBody::V1(p)
216    }
217}
218
219impl From<ProposalBodyV2> for VersionedProposalBody {
220    fn from(p: ProposalBodyV2) -> Self {
221        VersionedProposalBody::V2(p)
222    }
223}
224
225impl VersionedProposalBody {
226    pub fn latest_version(self) -> ProposalBodyV2 {
227        self.into()
228    }
229}
230
231pub fn default_categories() -> Vec<String> {
232    vec![
233        String::from("DevDAO Operations"),
234        String::from("Decentralized DevRel"),
235        String::from("NEAR Campus"),
236        String::from("Marketing"),
237        String::from("Events"),
238        String::from("Tooling & Infrastructures"),
239        String::from("Other"),
240    ]
241}
242
243#[near(serializers=[borsh, json])]
244#[derive(Clone, Debug)]
245pub enum ProposalFundingCurrency {
246    NEAR,
247    USDT,
248    USDC,
249    OTHER,
250}