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 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}