Skip to main content

shieldcontract/token_standards/
erc20.rs

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        // Check for required functions
19        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        // Check for required events
45        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}