1use std::collections::HashMap;
2
3use serde_derive::Deserialize;
4use serde_derive::Serialize;
5
6mod example;
7
8#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct Audit {
11 pub metadata: Metadata,
12 pub revisions: Vec<Revision>,
13 pub contracts: Vec<Contract>,
14 pub audit: AuditDetails,
15}
16
17#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
18#[serde(rename_all = "camelCase")]
19pub struct Metadata {
20 pub title: String,
21 pub source: String,
22 pub status: String,
23 pub languages: Vec<String>,
24 pub categories: Vec<String>,
25 pub tags: Vec<String>,
26 pub networks: Vec<String>,
27 pub timestamps: AuditTimestamps,
28 pub project: Project,
29}
30
31#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
32#[serde(rename_all = "camelCase")]
33pub struct AuditTimestamps {
34 pub onboarded: i64,
35 pub commencement: i64,
36 pub completion: i64,
37}
38
39#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct Project {
42 pub title: String,
43 pub website: String,
44 pub twitter: String,
45 pub github: String,
46}
47
48#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
49#[serde(rename_all = "camelCase")]
50pub struct Revision {
51 pub id: String,
52 pub title: String,
53 #[serde(rename = "source.commit")]
54 pub source_commit: String,
55 pub timestamps: Timestamps,
56 pub status: String,
57}
58
59#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
60#[serde(rename_all = "camelCase")]
61pub struct Contract {
62 pub id: String,
63 pub revisions: HashMap<String, ContractRevisionDetails>,
64 pub metadata: ContractMetadata,
65}
66
67#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
68#[serde(rename_all = "camelCase")]
69pub struct ContractRevisionDetails {
70 pub file: String,
71 pub name: String,
72}
73
74#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct ContractMetadata {
77 pub livematch: Livematch,
78 pub privileged: Vec<String>,
79}
80
81#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct Livematch {
84 pub link: String,
85 pub status: String,
86 pub timestamps: Timestamps,
87}
88
89#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct Timestamps {
92 pub commencement: i64,
93 pub completion: i64,
94}
95
96#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase")]
98pub struct AuditDetails {
99 pub contracts: HashMap<String, ContractAudit>,
100}
101
102#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
103#[serde(rename_all = "camelCase")]
104pub struct ContractAudit {
105 pub description: String,
106 pub issues: Issues,
107}
108
109#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
110#[serde(rename_all = "camelCase")]
111pub struct Issues {
112 pub count: i64,
113 pub severities: Severities,
114}
115
116#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
117#[serde(rename_all = "camelCase")]
118pub struct Severities {
119 pub info: Info,
120}
121
122#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
123#[serde(rename_all = "camelCase")]
124pub struct Info {
125 pub count: i64,
126 pub issues: Vec<Issue>,
127}
128
129#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
130#[serde(rename_all = "camelCase")]
131pub struct Issue {
132 pub id: String,
133 pub title: String,
134 pub description: String,
135 pub recommendation: String,
136 pub resolution: String,
137 pub status: String,
138 pub revisions: Revisions,
139 pub timestamps: Timestamps,
140}
141
142#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
143#[serde(rename_all = "camelCase")]
144pub struct Revisions {
145 pub raised: String,
146 pub resolved: String,
147}
148
149
150
151#[cfg(test)]
152mod tests {
153 use serde_json;
154
155 #[test]
156 fn it_works() {
157 let json_str = crate::example::tests::get_json();
158 let audit: crate::Audit = serde_json::from_str(&json_str).unwrap();
159 assert_eq!(audit.metadata.title, "XFarm Vaults");
160 assert_eq!(audit.metadata.source, "https://github.com/xfarms/vaults");
161 assert_eq!(audit.metadata.status, "auditing");
162 assert_eq!(audit.metadata.languages, vec!["solidity"]);
163 assert_eq!(audit.metadata.categories, vec!["vaults"]);
164 assert_eq!(audit.metadata.tags, vec!["medium"]);
165 assert_eq!(audit.metadata.networks, vec!["bsc"]);
166 assert_eq!(audit.metadata.timestamps.onboarded, 12312312312);
167 assert_eq!(audit.metadata.timestamps.commencement, 12313212312);
168 assert_eq!(audit.metadata.timestamps.completion, 12312321312);
169 assert_eq!(audit.metadata.project.title, "XFarm");
170 assert_eq!(audit.metadata.project.website, "https://xfarms.io");
171 assert_eq!(audit.metadata.project.twitter, "uno_farm");
172 assert_eq!(audit.metadata.project.github, "https://github.com/xfarms");
173 assert_eq!(audit.revisions.len(), 1);
174 let revision = audit.revisions[0].clone();
175 assert_eq!(revision.id, "preliminary");
176 assert_eq!(revision.title, "Preliminary");
177 assert_eq!(revision.source_commit, "https://github.com/xfarms/vaults/tree/4a6a247e017f96cb5f569331ca64c7f5fd41189d");
178 assert_eq!(revision.status, "auditing");
179 assert_eq!(revision.timestamps.commencement, 12312312312);
180 assert_eq!(revision.timestamps.completion, 1231232131);
181
182 assert_eq!(audit.contracts.len(), 1);
183 let contract = audit.contracts[0].clone();
184 assert_eq!(contract.id, "farm");
185 assert_eq!(contract.revisions.len(), 1);
186 let contract_revision = contract.revisions["preliminary"].clone();
187 assert_eq!(contract_revision.file, "contracts/Farm.sol");
188 assert_eq!(contract_revision.name, "Farm");
189 assert_eq!(contract.metadata.livematch.link, "https://bscscan.com/address/0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000");
190 assert_eq!(contract.metadata.livematch.status, "MATCHED");
191 assert_eq!(contract.metadata.livematch.timestamps.commencement, 123123213);
192 assert_eq!(contract.metadata.livematch.timestamps.completion, 12312312312);
193 assert_eq!(contract.metadata.privileged.len(), 4);
194 assert_eq!(contract.metadata.privileged[0], "transferOwnership");
195 assert_eq!(contract.metadata.privileged[1], "renounceOwnership");
196 assert_eq!(contract.metadata.privileged[2], "pause");
197 assert_eq!(contract.metadata.privileged[3], "unpause");
198
199 assert_eq!(audit.audit.contracts.len(), 1);
200 let farm = audit.audit.contracts["farm"].clone();
201 assert_eq!(farm.description, "The Farm contract is responsible for blabla");
202 assert_eq!(farm.issues.count, 1);
203 assert_eq!(farm.issues.severities.info.count, 1);
204 assert_eq!(farm.issues.severities.info.issues.len(), 1);
205 let issue = farm.issues.severities.info.issues[0].clone();
206 assert_eq!(issue.id, "farm-typographicerrors");
207 assert_eq!(issue.title, "Typographic Errors");
208 assert_eq!(issue.description, "The contract contains the following typographic errors:\n\nLine 3\n ```uint256 precsicion```");
209 assert_eq!(issue.recommendation, "Consider resolving the aforementioned errors.");
210 assert_eq!(issue.resolution, "");
211 assert_eq!(issue.status, "pending");
212 assert_eq!(issue.revisions.raised, "preliminary");
213 assert_eq!(issue.revisions.resolved, "resolutions1");
214 assert_eq!(issue.timestamps.commencement, 12312321);
215 assert_eq!(issue.timestamps.completion, 123123123);
216 }
217}