1use crate::token_standards::{
2 EventParameter, EventSignature, FunctionSignature, Parameter, TokenStandard,
3};
4use crate::{Finding, Result, Severity};
5use regex::Regex;
6
7#[derive(Debug, Clone)]
8pub struct ERC20Validator;
9
10impl ERC20Validator {
11 pub fn new() -> Self {
12 Self
13 }
14
15 pub fn validate(&self, content: &str) -> Result<Vec<Finding>> {
16 let mut findings = vec![];
17
18 for func in self.required_functions() {
20 if !self.function_exists(content, &func) {
21 findings.push(Finding {
22 id: format!("ERC20-MISSING-{}", func.name.to_uppercase()),
23 severity: Severity::High,
24 category: "token-standards/erc20".to_string(),
25 title: format!("Missing required ERC-20 function: {}", func.name),
26 description: format!(
27 "ERC-20 standard requires implementation of {} function",
28 func.name
29 ),
30 file: String::new(),
31 line: 0,
32 column: 0,
33 code_snippet: None,
34 remediation: Some(format!(
35 "Implement the {} function according to ERC-20 standard",
36 func.name
37 )),
38 references: vec!["https://eips.ethereum.org/EIPS/eip-20".to_string()],
39 ai_consensus: None,
40 });
41 }
42 }
43
44 for event in self.required_events() {
46 if !self.event_exists(content, &event) {
47 findings.push(Finding {
48 id: format!("ERC20-MISSING-EVENT-{}", event.name.to_uppercase()),
49 severity: Severity::High,
50 category: "token-standards/erc20".to_string(),
51 title: format!("Missing required ERC-20 event: {}", event.name),
52 description: format!(
53 "ERC-20 standard requires {} event to be defined",
54 event.name
55 ),
56 file: String::new(),
57 line: 0,
58 column: 0,
59 code_snippet: None,
60 remediation: Some(format!("Define the {} event", event.name)),
61 references: vec!["https://eips.ethereum.org/EIPS/eip-20".to_string()],
62 ai_consensus: None,
63 });
64 }
65 }
66
67 Ok(findings)
68 }
69
70 fn function_exists(&self, content: &str, func: &FunctionSignature) -> bool {
71 let pattern = format!(r"function\s+{}\s*\(", regex::escape(&func.name));
72 match crate::utils::create_regex(&pattern) {
73 Ok(regex) => regex.is_match(content),
74 Err(_) => false,
75 }
76 }
77
78 fn event_exists(&self, content: &str, event: &EventSignature) -> bool {
79 let pattern = format!(r"event\s+{}\s*\(", regex::escape(&event.name));
80 match crate::utils::create_regex(&pattern) {
81 Ok(regex) => regex.is_match(content),
82 Err(_) => false,
83 }
84 }
85}
86
87impl TokenStandard for ERC20Validator {
88 fn validate(&self, code: &str, _language: &str) -> Result<Vec<Finding>> {
89 self.validate(code)
90 }
91
92 fn name(&self) -> &str {
93 "ERC-20"
94 }
95
96 fn required_functions(&self) -> Vec<FunctionSignature> {
97 vec![
98 FunctionSignature {
99 name: "totalSupply".to_string(),
100 parameters: vec![],
101 returns: vec![Parameter {
102 name: "".to_string(),
103 type_: "uint256".to_string(),
104 }],
105 visibility: "public".to_string(),
106 mutability: "view".to_string(),
107 },
108 FunctionSignature {
109 name: "balanceOf".to_string(),
110 parameters: vec![Parameter {
111 name: "_owner".to_string(),
112 type_: "address".to_string(),
113 }],
114 returns: vec![Parameter {
115 name: "balance".to_string(),
116 type_: "uint256".to_string(),
117 }],
118 visibility: "public".to_string(),
119 mutability: "view".to_string(),
120 },
121 FunctionSignature {
122 name: "transfer".to_string(),
123 parameters: vec![
124 Parameter {
125 name: "_to".to_string(),
126 type_: "address".to_string(),
127 },
128 Parameter {
129 name: "_value".to_string(),
130 type_: "uint256".to_string(),
131 },
132 ],
133 returns: vec![Parameter {
134 name: "success".to_string(),
135 type_: "bool".to_string(),
136 }],
137 visibility: "public".to_string(),
138 mutability: "nonpayable".to_string(),
139 },
140 FunctionSignature {
141 name: "transferFrom".to_string(),
142 parameters: vec![
143 Parameter {
144 name: "_from".to_string(),
145 type_: "address".to_string(),
146 },
147 Parameter {
148 name: "_to".to_string(),
149 type_: "address".to_string(),
150 },
151 Parameter {
152 name: "_value".to_string(),
153 type_: "uint256".to_string(),
154 },
155 ],
156 returns: vec![Parameter {
157 name: "success".to_string(),
158 type_: "bool".to_string(),
159 }],
160 visibility: "public".to_string(),
161 mutability: "nonpayable".to_string(),
162 },
163 FunctionSignature {
164 name: "approve".to_string(),
165 parameters: vec![
166 Parameter {
167 name: "_spender".to_string(),
168 type_: "address".to_string(),
169 },
170 Parameter {
171 name: "_value".to_string(),
172 type_: "uint256".to_string(),
173 },
174 ],
175 returns: vec![Parameter {
176 name: "success".to_string(),
177 type_: "bool".to_string(),
178 }],
179 visibility: "public".to_string(),
180 mutability: "nonpayable".to_string(),
181 },
182 FunctionSignature {
183 name: "allowance".to_string(),
184 parameters: vec![
185 Parameter {
186 name: "_owner".to_string(),
187 type_: "address".to_string(),
188 },
189 Parameter {
190 name: "_spender".to_string(),
191 type_: "address".to_string(),
192 },
193 ],
194 returns: vec![Parameter {
195 name: "remaining".to_string(),
196 type_: "uint256".to_string(),
197 }],
198 visibility: "public".to_string(),
199 mutability: "view".to_string(),
200 },
201 ]
202 }
203
204 fn required_events(&self) -> Vec<EventSignature> {
205 vec![
206 EventSignature {
207 name: "Transfer".to_string(),
208 parameters: vec![
209 EventParameter {
210 name: "_from".to_string(),
211 type_: "address".to_string(),
212 indexed: true,
213 },
214 EventParameter {
215 name: "_to".to_string(),
216 type_: "address".to_string(),
217 indexed: true,
218 },
219 EventParameter {
220 name: "_value".to_string(),
221 type_: "uint256".to_string(),
222 indexed: false,
223 },
224 ],
225 },
226 EventSignature {
227 name: "Approval".to_string(),
228 parameters: vec![
229 EventParameter {
230 name: "_owner".to_string(),
231 type_: "address".to_string(),
232 indexed: true,
233 },
234 EventParameter {
235 name: "_spender".to_string(),
236 type_: "address".to_string(),
237 indexed: true,
238 },
239 EventParameter {
240 name: "_value".to_string(),
241 type_: "uint256".to_string(),
242 indexed: false,
243 },
244 ],
245 },
246 ]
247 }
248
249 fn optional_functions(&self) -> Vec<FunctionSignature> {
250 vec![
251 FunctionSignature {
252 name: "name".to_string(),
253 parameters: vec![],
254 returns: vec![Parameter {
255 name: "".to_string(),
256 type_: "string".to_string(),
257 }],
258 visibility: "public".to_string(),
259 mutability: "view".to_string(),
260 },
261 FunctionSignature {
262 name: "symbol".to_string(),
263 parameters: vec![],
264 returns: vec![Parameter {
265 name: "".to_string(),
266 type_: "string".to_string(),
267 }],
268 visibility: "public".to_string(),
269 mutability: "view".to_string(),
270 },
271 FunctionSignature {
272 name: "decimals".to_string(),
273 parameters: vec![],
274 returns: vec![Parameter {
275 name: "".to_string(),
276 type_: "uint8".to_string(),
277 }],
278 visibility: "public".to_string(),
279 mutability: "view".to_string(),
280 },
281 ]
282 }
283}