Skip to content
This repository has been archived by the owner on Jul 3, 2024. It is now read-only.

105 - Payable Fallback Rule #147

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod global_import;
pub mod max_states_count;
pub mod no_console;
pub mod one_contract_per_file;
pub mod payable_fallback;
pub mod reason_string;
mod explicit_types;

Expand All @@ -24,6 +25,7 @@ use crate::rules::best_practises::line_maxlen::LineMaxLen;
use crate::rules::best_practises::max_states_count::MaxStatesCount;
use crate::rules::best_practises::no_console::NoConsole;
use crate::rules::best_practises::one_contract_per_file::OneContractPerFile;
use crate::rules::best_practises::payable_fallback::PayableFallback;
use crate::rules::best_practises::reason_string::ReasonString;
use crate::rules::RuleBuilder;

Expand All @@ -39,6 +41,7 @@ pub fn create_default_rules() -> Vec<RuleEntry> {
GlobalImport::create_default(),
EmptyBlock::create_default(),
ExplicitTypes::create_default(),
PayableFallback::create_default(),
]
}

Expand All @@ -65,6 +68,10 @@ pub fn create_rules() -> RulesMap {
rules.insert(empty_block::RULE_ID.to_string(), EmptyBlock::create);
rules.insert(explicit_types::RULE_ID.to_string(), ExplicitTypes::create);
rules.insert(no_console::RULE_ID.to_string(), NoConsole::create);
rules.insert(
payable_fallback::RULE_ID.to_string(),
PayableFallback::create,
);

rules
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use ast_extractor::retriever::{retrieve_contract_nodes, retrieve_functions_nodes};
use ast_extractor::{FunctionKind, ItemFunction, Mutability, Spanned};

use crate::linter::SolidFile;
use crate::rules::types::*;
use crate::types::*;

// const DEFAULT_SEVERITY: &str = "warn";
const DEFAULT_MESSAGE: &str = "Fallback should contains payable attributes";
pub const RULE_ID: &str = "payable-fallback";

pub struct PayableFallback {
_data: RuleEntry,
}

impl RuleType for PayableFallback {
fn diagnose(&self, _file: &SolidFile, _files: &[SolidFile]) -> Vec<LintDiag> {
let mut res = Vec::new();
let reports = check_fallback_payable(_file);

for report in reports.into_iter().flatten() {
res.push(LintDiag {
id: RULE_ID.to_string(),
severity: Some(Severity::WARNING),
range: report,
code: None,
source: None,
message: DEFAULT_MESSAGE.to_string(),
uri: _file.path.clone(),
source_file_content: _file.content.clone(),
});
}
res
}
}

fn check_fallback_payable(file: &SolidFile) -> Vec<Option<Range>> {
let mut res: Vec<Option<Range>> = Vec::new();

let contracts = retrieve_contract_nodes(&file.data);
for contract in contracts {
let functions = retrieve_functions_nodes(&contract);

for function in functions {
if FunctionKind::is_fallback(function.kind) || function.name.is_none() {
res = check_attribute(res, function);
}
}
}
res
}
fn check_attribute(mut res: Vec<Option<Range>>, function: ItemFunction) -> Vec<Option<Range>> {
let mut is_payable = false;
for attributes in function.attributes.iter() {
if attributes.mutability().is_some()
&& Mutability::is_payable(attributes.mutability().unwrap())
{
is_payable = true;
}
}
if !is_payable {
res.push(create_report(function));
}
res
}

fn create_report(function: ItemFunction) -> Option<Range> {
Some(Range {
start: Position {
line: function.attributes.span().start().line,
character: function.attributes.span().start().column + 1,
},
end: Position {
line: function.attributes.span().end().line,
character: function.attributes.span().end().column,
},
})
}

impl PayableFallback {
pub fn create(data: RuleEntry) -> Box<dyn RuleType> {
let rule = PayableFallback { _data: data };
Box::new(rule)
}

pub fn create_default() -> RuleEntry {
RuleEntry {
id: RULE_ID.to_string(),
severity: Severity::WARNING,
data: vec![],
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"name": "solidhunter",
"includes": [],
"plugins": [],
"rules": [
{
"id": "line-max-len",
"severity": "WARNING",
"data": [
"80"
]
},
{
"id": "max-states-count",
"severity": "WARNING",
"data": [
"15"
]
},
{
"id": "function-max-lines",
"severity": "WARNING",
"data": [
"20"
]
},
{
"id": "reason-string",
"severity": "WARNING",
"data": [
"32"
]
},
{
"id": "contract-name-pascalcase",
"severity": "WARNING",
"data": []
},
{
"id": "func-name-camelcase",
"severity": "WARNING",
"data": []
},
{
"id": "func-param-name-camelcase",
"severity": "WARNING",
"data": []
},
{
"id": "use-forbidden-name",
"severity": "WARNING",
"data": []
},
{
"id": "import-on-top",
"severity": "WARNING",
"data": []
},
{
"id": "payable-fallback",
"severity": "WARNING",
"data": []
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pragma solidity 0.8.19;

contract Test {
function() public payable {} // Valid

fallback() external payable {} // Valid

function() public {} // Not Valid

fallback() external {} // Not Valid
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
payable-fallback:8:16:8:21
payable-fallback:10:16:10:23
Original file line number Diff line number Diff line change
Expand Up @@ -160,5 +160,6 @@ test_directories! {
NoConsole,
ExplicitTypes,
ImplicitTypes,
PayableFallback,
GlobalImport
}