mirror of https://github.com/crytic/slither
commit
e579fce61e
@ -0,0 +1,54 @@ |
||||
import os |
||||
import logging |
||||
import argparse |
||||
from slither import Slither |
||||
from slither.printers.all_printers import PrinterCallGraph |
||||
from slither.core.declarations.function import Function |
||||
|
||||
logging.basicConfig() |
||||
logging.getLogger("Slither").setLevel(logging.INFO) |
||||
logging.getLogger("Printers").setLevel(logging.INFO) |
||||
|
||||
class PrinterCallGraphStateChange(PrinterCallGraph): |
||||
|
||||
def _process_function(self, contract, function, contract_functions, contract_calls, solidity_functions, solidity_calls, external_calls, all_contracts): |
||||
if function.view or function.pure: |
||||
return |
||||
super()._process_function(contract, function, contract_functions, contract_calls, solidity_functions, solidity_calls, external_calls, all_contracts) |
||||
|
||||
def _process_internal_call(self, contract, function, internal_call, contract_calls, solidity_functions, solidity_calls): |
||||
if isinstance(internal_call, Function): |
||||
if internal_call.view or internal_call.pure: |
||||
return |
||||
super()._process_internal_call(contract, function, internal_call, contract_calls, solidity_functions, solidity_calls) |
||||
|
||||
def _process_external_call(self, contract, function, external_call, contract_functions, external_calls, all_contracts): |
||||
if isinstance(external_call[1], Function): |
||||
if external_call[1].view or external_call[1].pure: |
||||
return |
||||
super()._process_external_call(contract, function, external_call, contract_functions, external_calls, all_contracts) |
||||
|
||||
def parse_args(): |
||||
""" |
||||
""" |
||||
parser = argparse.ArgumentParser(description='Call graph printer. Similar to --print call-graph, but without printing the view/pure functions', |
||||
usage='call_graph.py filename') |
||||
|
||||
parser.add_argument('filename', |
||||
help='The filename of the contract or truffle directory to analyze.') |
||||
|
||||
parser.add_argument('--solc', help='solc path', default='solc') |
||||
|
||||
return parser.parse_args() |
||||
|
||||
def main(): |
||||
|
||||
args = parse_args() |
||||
slither = Slither(args.filename, is_truffle=os.path.isdir(args.filename), solc=args.solc) |
||||
|
||||
slither.register_printer(PrinterCallGraphStateChange) |
||||
|
||||
slither.run_printers() |
||||
|
||||
if __name__ == '__main__': |
||||
main() |
@ -0,0 +1,16 @@ |
||||
#!/usr/bin/env bash |
||||
|
||||
### Test slither-check-upgradability |
||||
|
||||
DIR_TESTS="tests/possible_paths" |
||||
|
||||
slither-find-paths "$DIR_TESTS/paths.sol" A.destination --solc solc-0.5.0 > test_possible_paths.txt 2>&1 |
||||
DIFF=$(diff test_possible_paths.txt "$DIR_TESTS/paths.txt") |
||||
if [ "$DIFF" != "" ] |
||||
then |
||||
echo "slither-find-paths failed" |
||||
cat test_possible_paths.txt |
||||
cat "$DIR_TESTS/paths.txt" |
||||
exit -1 |
||||
fi |
||||
rm test_possible_paths.txt |
@ -0,0 +1,49 @@ |
||||
contract Initializable{ |
||||
|
||||
address destination; |
||||
|
||||
modifier initializer(){ |
||||
_; |
||||
} |
||||
|
||||
} |
||||
|
||||
contract Contract_no_bug is Initializable{ |
||||
|
||||
function initialize() public initializer{ |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
contract Contract_lack_to_call_modifier is Initializable{ |
||||
|
||||
function initialize() public { |
||||
|
||||
} |
||||
} |
||||
|
||||
contract Contract_not_called_super_init is Contract_no_bug{ |
||||
|
||||
function initialize() public initializer{ |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
contract Contract_no_bug_inherits is Contract_no_bug{ |
||||
|
||||
function initialize() public initializer{ |
||||
Contract_no_bug.initialize(); |
||||
} |
||||
|
||||
} |
||||
|
||||
contract Contract_double_call is Contract_no_bug, Contract_no_bug_inherits{ |
||||
|
||||
function initialize() public initializer{ |
||||
Contract_no_bug_inherits.initialize(); |
||||
Contract_no_bug.initialize(); |
||||
} |
||||
|
||||
} |
@ -1 +1 @@ |
||||
[{"check": "arbitrary-send", "impact": "High", "confidence": "Medium", "description": "Test.direct (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbirary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12)\n", "elements": [{"type": "function", "name": "direct", "source_mapping": {"start": 162, "length": 79, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [11, 12, 13]}, "contract": {"type": "contract", "name": "Test", "source_mapping": {"start": 0, "length": 884, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]}}}, {"type": "expression", "expression": "msg.sender.send(address(this).balance)", "source_mapping": {"start": 196, "length": 38, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [12]}}]}, {"check": "arbitrary-send", "impact": "High", "confidence": "Medium", "description": "Test.indirect (tests/arbitrary_send-0.5.1.sol#19-21) sends eth to arbirary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20)\n", "elements": [{"type": "function", "name": "indirect", "source_mapping": {"start": 316, "length": 82, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [19, 20, 21]}, "contract": {"type": "contract", "name": "Test", "source_mapping": {"start": 0, "length": 884, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]}}}, {"type": "expression", "expression": "destination.send(address(this).balance)", "source_mapping": {"start": 352, "length": 39, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [20]}}]}] |
||||
[{"check": "arbitrary-send", "impact": "High", "confidence": "Medium", "description": "Test.direct (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbitrary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12)\n", "elements": [{"type": "function", "name": "direct", "source_mapping": {"start": 162, "length": 79, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [11, 12, 13]}, "contract": {"type": "contract", "name": "Test", "source_mapping": {"start": 0, "length": 884, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]}}}, {"type": "expression", "expression": "msg.sender.send(address(this).balance)", "source_mapping": {"start": 196, "length": 38, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [12]}}]}, {"check": "arbitrary-send", "impact": "High", "confidence": "Medium", "description": "Test.indirect (tests/arbitrary_send-0.5.1.sol#19-21) sends eth to arbitrary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20)\n", "elements": [{"type": "function", "name": "indirect", "source_mapping": {"start": 316, "length": 82, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [19, 20, 21]}, "contract": {"type": "contract", "name": "Test", "source_mapping": {"start": 0, "length": 884, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]}}}, {"type": "expression", "expression": "destination.send(address(this).balance)", "source_mapping": {"start": 352, "length": 39, "filename": "tests/arbitrary_send-0.5.1.sol", "lines": [20]}}]}] |
@ -1 +1 @@ |
||||
[{"check": "arbitrary-send", "impact": "High", "confidence": "Medium", "description": "Test.direct (tests/arbitrary_send.sol#11-13) sends eth to arbirary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12)\n", "elements": [{"type": "function", "name": "direct", "source_mapping": {"start": 147, "length": 79, "filename": "tests/arbitrary_send.sol", "lines": [11, 12, 13]}, "contract": {"type": "contract", "name": "Test", "source_mapping": {"start": 0, "length": 869, "filename": "tests/arbitrary_send.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]}}}, {"type": "expression", "expression": "msg.sender.send(address(this).balance)", "source_mapping": {"start": 181, "length": 38, "filename": "tests/arbitrary_send.sol", "lines": [12]}}]}, {"check": "arbitrary-send", "impact": "High", "confidence": "Medium", "description": "Test.indirect (tests/arbitrary_send.sol#19-21) sends eth to arbirary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send.sol#20)\n", "elements": [{"type": "function", "name": "indirect", "source_mapping": {"start": 301, "length": 82, "filename": "tests/arbitrary_send.sol", "lines": [19, 20, 21]}, "contract": {"type": "contract", "name": "Test", "source_mapping": {"start": 0, "length": 869, "filename": "tests/arbitrary_send.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]}}}, {"type": "expression", "expression": "destination.send(address(this).balance)", "source_mapping": {"start": 337, "length": 39, "filename": "tests/arbitrary_send.sol", "lines": [20]}}]}] |
||||
[{"check": "arbitrary-send", "impact": "High", "confidence": "Medium", "description": "Test.direct (tests/arbitrary_send.sol#11-13) sends eth to arbitrary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12)\n", "elements": [{"type": "function", "name": "direct", "source_mapping": {"start": 147, "length": 79, "filename": "tests/arbitrary_send.sol", "lines": [11, 12, 13]}, "contract": {"type": "contract", "name": "Test", "source_mapping": {"start": 0, "length": 869, "filename": "tests/arbitrary_send.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]}}}, {"type": "expression", "expression": "msg.sender.send(address(this).balance)", "source_mapping": {"start": 181, "length": 38, "filename": "tests/arbitrary_send.sol", "lines": [12]}}]}, {"check": "arbitrary-send", "impact": "High", "confidence": "Medium", "description": "Test.indirect (tests/arbitrary_send.sol#19-21) sends eth to arbitrary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send.sol#20)\n", "elements": [{"type": "function", "name": "indirect", "source_mapping": {"start": 301, "length": 82, "filename": "tests/arbitrary_send.sol", "lines": [19, 20, 21]}, "contract": {"type": "contract", "name": "Test", "source_mapping": {"start": 0, "length": 869, "filename": "tests/arbitrary_send.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]}}}, {"type": "expression", "expression": "destination.send(address(this).balance)", "source_mapping": {"start": 337, "length": 39, "filename": "tests/arbitrary_send.sol", "lines": [20]}}]}] |
@ -0,0 +1,17 @@ |
||||
contract A{ |
||||
function destination() private{ |
||||
|
||||
} |
||||
|
||||
function call() public{ |
||||
destination(); |
||||
} |
||||
} |
||||
|
||||
contract B{ |
||||
|
||||
|
||||
function call2(A a) public{ |
||||
a.call(); |
||||
} |
||||
} |
@ -0,0 +1,14 @@ |
||||
Target functions: |
||||
- A.destination() |
||||
|
||||
|
||||
The following functions reach the specified targets: |
||||
- A.call() |
||||
- B.call2(A) |
||||
|
||||
|
||||
The following paths reach the specified targets: |
||||
A.call() -> A.destination() |
||||
|
||||
B.call2(A) -> A.call() -> A.destination() |
||||
|
@ -0,0 +1,72 @@ |
||||
import os |
||||
import argparse |
||||
from slither import Slither |
||||
from slither.utils.colors import red |
||||
import logging |
||||
from .possible_paths import find_target_paths, resolve_functions, ResolveFunctionException |
||||
|
||||
logging.basicConfig() |
||||
logging.getLogger("Slither").setLevel(logging.INFO) |
||||
|
||||
def parse_args(): |
||||
""" |
||||
Parse the underlying arguments for the program. |
||||
:return: Returns the arguments for the program. |
||||
""" |
||||
parser = argparse.ArgumentParser(description='PossiblePaths', |
||||
usage='possible_paths.py filename [contract.function targets]') |
||||
|
||||
parser.add_argument('filename', |
||||
help='The filename of the contract or truffle directory to analyze.') |
||||
|
||||
parser.add_argument('targets', nargs='+') |
||||
|
||||
parser.add_argument('--solc', help='solc path', default='solc') |
||||
|
||||
return parser.parse_args() |
||||
|
||||
|
||||
def main(): |
||||
# ------------------------------ |
||||
# PossiblePaths.py |
||||
# Usage: python3 possible_paths.py filename targets |
||||
# Example: python3 possible_paths.py contract.sol contract1.function1 contract2.function2 contract3.function3 |
||||
# ------------------------------ |
||||
# Parse all arguments |
||||
args = parse_args() |
||||
|
||||
# Perform slither analysis on the given filename |
||||
slither = Slither(args.filename, is_truffle=os.path.isdir(args.filename), solc=args.solc, disable_solc_warnings=True) |
||||
|
||||
try: |
||||
targets = resolve_functions(slither, args.targets) |
||||
except ResolveFunctionException as r: |
||||
print(red(r)) |
||||
exit(-1) |
||||
|
||||
# Print out all target functions. |
||||
print(f"Target functions:") |
||||
for target in targets: |
||||
print(f"- {target.contract.name}.{target.full_name}") |
||||
print("\n") |
||||
|
||||
# Obtain all paths which reach the target functions. |
||||
reaching_paths = find_target_paths(slither, targets) |
||||
reaching_functions = set([y for x in reaching_paths for y in x if y not in targets]) |
||||
|
||||
# Print out all function names which can reach the targets. |
||||
print(f"The following functions reach the specified targets:") |
||||
for function_desc in sorted([f"{f.contract.name}.{f.full_name}" for f in reaching_functions]): |
||||
print(f"- {function_desc}") |
||||
print("\n") |
||||
|
||||
# Format all function paths. |
||||
reaching_paths_str = [' -> '.join([f"{f.contract.name}.{f.full_name}" for f in reaching_path]) for reaching_path in reaching_paths] |
||||
|
||||
# Print a sorted list of all function paths which can reach the targets. |
||||
print(f"The following paths reach the specified targets:") |
||||
for reaching_path in sorted(reaching_paths_str): |
||||
print(f"{reaching_path}\n") |
||||
|
||||
if __name__ == '__main__': |
||||
main() |
@ -0,0 +1,127 @@ |
||||
class ResolveFunctionException(Exception): pass |
||||
|
||||
|
||||
def resolve_function(slither, contract_name, function_name): |
||||
""" |
||||
Resolves a function instance, given a contract name and function. |
||||
:param contract_name: The name of the contract the function is declared in. |
||||
:param function_name: The name of the function to resolve. |
||||
:return: Returns the resolved function, raises an exception otherwise. |
||||
""" |
||||
# Obtain the target contract |
||||
contract = slither.get_contract_from_name(contract_name) |
||||
|
||||
# Verify the contract was resolved successfully |
||||
if contract is None: |
||||
raise ResolveFunctionException(f"Could not resolve target contract: {contract_name}") |
||||
|
||||
# Obtain the target function |
||||
target_function = next((function for function in contract.functions if function.name == function_name), None) |
||||
|
||||
# Verify we have resolved the function specified. |
||||
if target_function is None: |
||||
raise ResolveFunctionException(f"Could not resolve target function: {contract_name}.{function_name}") |
||||
|
||||
# Add the resolved function to the new list. |
||||
return target_function |
||||
|
||||
|
||||
def resolve_functions(slither, functions): |
||||
""" |
||||
Resolves the provided function descriptors. |
||||
:param functions: A list of tuples (contract_name, function_name) or str (of form "ContractName.FunctionName") |
||||
to resolve into function objects. |
||||
:return: Returns a list of resolved functions. |
||||
""" |
||||
# Create the resolved list. |
||||
resolved = [] |
||||
|
||||
# Verify that the provided argument is a list. |
||||
if not isinstance(functions, list): |
||||
raise ResolveFunctionException("Provided functions to resolve must be a list type.") |
||||
|
||||
# Loop for each item in the list. |
||||
for item in functions: |
||||
if isinstance(item, str): |
||||
# If the item is a single string, we assume it is of form 'ContractName.FunctionName'. |
||||
parts = item.split('.') |
||||
if len(parts) < 2: |
||||
raise ResolveFunctionException("Provided string descriptor must be of form 'ContractName.FunctionName'") |
||||
resolved.append(resolve_function(slither, parts[0], parts[1])) |
||||
elif isinstance(item, tuple): |
||||
# If the item is a tuple, it should be a 2-tuple providing contract and function names. |
||||
if len(item) != 2: |
||||
raise ResolveFunctionException("Provided tuple descriptor must provide a contract and function name.") |
||||
resolved.append(resolve_function(slither, item[0], item[1])) |
||||
else: |
||||
raise ResolveFunctionException(f"Unexpected function descriptor type to resolve in list: {type(item)}") |
||||
|
||||
# Return the resolved list. |
||||
return resolved |
||||
|
||||
|
||||
def all_function_definitions(function): |
||||
""" |
||||
Obtains a list of representing this function and any base definitions |
||||
:param function: The function to obtain all definitions at and beneath. |
||||
:return: Returns a list composed of the provided function definition and any base definitions. |
||||
""" |
||||
return [function] + [f for c in function.contract.inheritance |
||||
for f in c.functions_and_modifiers_not_inherited |
||||
if f.full_name == function.full_name] |
||||
|
||||
|
||||
def __find_target_paths(slither, target_function, current_path=[]): |
||||
|
||||
# Create our results list |
||||
results = set() |
||||
|
||||
# Add our current function to the path. |
||||
current_path = [target_function] + current_path |
||||
|
||||
# Obtain this target function and any base definitions. |
||||
all_target_functions = set(all_function_definitions(target_function)) |
||||
|
||||
# Look through all functions |
||||
for contract in slither.contracts: |
||||
for function in contract.functions_and_modifiers_not_inherited: |
||||
|
||||
# If the function is already in our path, skip it. |
||||
if function in current_path: |
||||
continue |
||||
|
||||
# Find all function calls in this function (except for low level) |
||||
called_functions = [f for (_, f) in function.high_level_calls + function.library_calls] |
||||
called_functions += function.internal_calls |
||||
called_functions = set(called_functions) |
||||
|
||||
# If any of our target functions are reachable from this function, it's a result. |
||||
if all_target_functions.intersection(called_functions): |
||||
path_results = __find_target_paths(slither, function, current_path.copy()) |
||||
if path_results: |
||||
results = results.union(path_results) |
||||
|
||||
# If this path is external accessible from this point, we add the current path to the list. |
||||
if target_function.visibility in ['public', 'external'] and len(current_path) > 1: |
||||
results.add(tuple(current_path)) |
||||
|
||||
return results |
||||
|
||||
|
||||
def find_target_paths(slither, target_functions): |
||||
""" |
||||
Obtains all functions which can lead to any of the target functions being called. |
||||
:param target_functions: The functions we are interested in reaching. |
||||
:return: Returns a list of all functions which can reach any of the target_functions. |
||||
""" |
||||
# Create our results list |
||||
results = set() |
||||
|
||||
# Loop for each target function |
||||
for target_function in target_functions: |
||||
results = results.union(__find_target_paths(slither, target_function)) |
||||
|
||||
return results |
||||
|
||||
|
||||
|
@ -0,0 +1,78 @@ |
||||
import logging |
||||
from slither import Slither |
||||
from slither.slithir.operations import InternalCall |
||||
from slither.utils.colors import green,red |
||||
from slither.utils.colors import red, yellow, green |
||||
|
||||
logger = logging.getLogger("CheckInitialization") |
||||
logger.setLevel(logging.INFO) |
||||
|
||||
class MultipleInitTarget(Exception): |
||||
pass |
||||
|
||||
def _get_initialize_functions(contract): |
||||
return [f for father in contract.inheritance + [contract] for f in father.functions_not_inherited if f.name == 'initialize'] |
||||
|
||||
def _get_all_internal_calls(function): |
||||
all_ir = function.all_slithir_operations() |
||||
return [i.function for i in all_ir if isinstance(i, InternalCall) and i.function_name == "initialize"] |
||||
|
||||
|
||||
def _get_most_derived_init(contract): |
||||
for c in [contract] + contract.inheritance: |
||||
init_functions = [f for f in c.functions_not_inherited if f.name == 'initialize'] |
||||
if len(init_functions) > 1: |
||||
raise MultipleInitTarget |
||||
if init_functions: |
||||
return init_functions[0] |
||||
return None |
||||
|
||||
def check_initialization(s): |
||||
|
||||
initializable = s.get_contract_from_name('Initializable') |
||||
|
||||
if initializable is None: |
||||
logger.info(yellow('Initializable contract not found, the contract does not follow a standard initalization schema.')) |
||||
return |
||||
|
||||
initializer = initializable.get_modifier_from_signature('initializer()') |
||||
|
||||
init_info = '' |
||||
|
||||
double_calls_found = False |
||||
missing_call = False |
||||
initializer_modifier_missing = False |
||||
|
||||
for contract in s.contracts: |
||||
if initializable in contract.inheritance: |
||||
all_init_functions = _get_initialize_functions(contract) |
||||
for f in all_init_functions: |
||||
if not initializer in f.modifiers: |
||||
initializer_modifier_missing = True |
||||
logger.info(red(f'{f.contract.name}.{f.name} does not call initializer')) |
||||
most_derived_init = _get_most_derived_init(contract) |
||||
if most_derived_init is None: |
||||
init_info += f'{contract.name} has no initialize function\n' |
||||
continue |
||||
else: |
||||
init_info += f'{contract.name} needs to be initialized by {most_derived_init.full_name}\n' |
||||
all_init_functions_called = _get_all_internal_calls(most_derived_init) + [most_derived_init] |
||||
missing_calls = [f for f in all_init_functions if not f in all_init_functions_called] |
||||
for f in missing_calls: |
||||
logger.info(red(f'Missing call to {f.contract.name}.{f.name} in {contract.name}')) |
||||
missing_call = True |
||||
double_calls = list(set([f for f in all_init_functions_called if all_init_functions_called.count(f) > 1])) |
||||
for f in double_calls: |
||||
logger.info(red(f'{f.contract.name + "." + f.full_name} is called multiple time in {contract.name}')) |
||||
double_calls_found = True |
||||
|
||||
if not initializer_modifier_missing: |
||||
logger.info(green('All the init functions have the initiliazer modifier')) |
||||
|
||||
if not double_calls_found: |
||||
logger.info(green('No double call to init functions found')) |
||||
|
||||
if not missing_call: |
||||
logger.info(green('No missing call to an init function found')) |
||||
|
||||
logger.info(green('Check the deployement script to ensure that these functions are called:\n'+ init_info)) |
Loading…
Reference in new issue