mirror of https://github.com/crytic/slither
All changes specific to slither-format tool, excluding the dependencies on slither parsing/core and detectors. Single commit because of checking out utils/slither_format from dev-slither-format into this branch.
parent
9341166e9a
commit
106928f911
@ -0,0 +1 @@ |
||||
*.format |
@ -0,0 +1,51 @@ |
||||
import os |
||||
import argparse |
||||
from slither import Slither |
||||
from slither.utils.colors import red |
||||
import logging |
||||
from .slither_format import slither_format |
||||
|
||||
logging.basicConfig() |
||||
logging.getLogger("Slither").setLevel(logging.INFO) |
||||
|
||||
available_detectors = ["external-function"] |
||||
detectors_to_run = [] |
||||
|
||||
def parse_args(): |
||||
""" |
||||
Parse the underlying arguments for the program. |
||||
:return: Returns the arguments for the program. |
||||
""" |
||||
parser = argparse.ArgumentParser(description='slither_format', |
||||
usage='slither_format 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') |
||||
parser.add_argument('--verbose', '-v', help='verbose mode output',action='store_true',default=False) |
||||
|
||||
group_detector = parser.add_argument_group('Detectors') |
||||
group_detector.add_argument('--detect', |
||||
help='Comma-separated list of detectors, defaults to all, ' |
||||
'available detectors: {}'.format( |
||||
', '.join(d for d in available_detectors)), |
||||
action='store', |
||||
dest='detectors_to_run', |
||||
default='all') |
||||
return parser.parse_args() |
||||
|
||||
|
||||
def main(): |
||||
# ------------------------------ |
||||
# Usage: python3 -m slither_format filename |
||||
# Example: python3 -m slither_format contract.sol |
||||
# ------------------------------ |
||||
# 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) |
||||
|
||||
# Format the input files based on slither analysis |
||||
slither_format(args, slither) |
||||
if __name__ == '__main__': |
||||
main() |
@ -0,0 +1,25 @@ |
||||
import re |
||||
|
||||
class FormatConstableStates: |
||||
|
||||
@staticmethod |
||||
def format(slither, patches, elements): |
||||
for element in elements: |
||||
FormatConstableStates.create_patch(slither, patches, element['source_mapping']['filename'], element['name'], "constant " + element['name'], element['source_mapping']['start'], element['source_mapping']['start'] + element['source_mapping']['length']) |
||||
|
||||
@staticmethod |
||||
def create_patch(slither, patches, in_file, match_text, replace_text, modify_loc_start, modify_loc_end): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
(new_str_of_interest, num_repl) = re.subn(match_text, replace_text, old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patches[in_file].append({ |
||||
"detector" : "constable-states", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_end, |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
}) |
||||
else: |
||||
print("Error: State variable not found?!") |
||||
sys.exit(-1) |
@ -0,0 +1,37 @@ |
||||
import re |
||||
|
||||
class FormatConstantFunction: |
||||
|
||||
@staticmethod |
||||
def format(slither, patches, elements): |
||||
for element in elements: |
||||
if element['type'] != "function": |
||||
# Skip variable elements |
||||
continue |
||||
Found = False |
||||
for contract in slither.contracts: |
||||
if not Found: |
||||
for function in contract.functions: |
||||
if contract.name == element['contract']['name'] and function.name == element['name']: |
||||
FormatConstantFunction.create_patch(slither, patches, element['source_mapping']['filename'], ["view","pure","constant"], "", int(function.parameters_src.split(':')[0]), int(function.returns_src.split(':')[0])) |
||||
Found = True |
||||
|
||||
@staticmethod |
||||
def create_patch(slither, patches, in_file, match_text, replace_text, modify_loc_start, modify_loc_end): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
for match_text_item in match_text: |
||||
(new_str_of_interest, num_repl) = re.subn(match_text_item, replace_text, old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
break |
||||
if num_repl != 0: |
||||
patches[in_file].append({ |
||||
"detector" : "constant-function", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_end, |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
}) |
||||
else: |
||||
print("Error: No view/pure/constant specifier exists. Regex failed to remove specifier!") |
||||
sys.exit(-1) |
@ -0,0 +1,59 @@ |
||||
import re |
||||
|
||||
class FormatExternalFunction: |
||||
|
||||
@staticmethod |
||||
def format (slither, patches, elements): |
||||
for element in elements: |
||||
Found = False |
||||
for contract in slither.contracts: |
||||
if not Found and contract.name == element['contract']['name']: |
||||
for function in contract.functions: |
||||
if function.name == element['name']: |
||||
# If function parameters are written to in function body then we cannot convert this function |
||||
# to external because external function parameters are allocated in calldata region which is |
||||
# non-modifiable. See https://solidity.readthedocs.io/en/develop/types.html#data-location |
||||
if not FormatExternalFunction.function_parameters_written(function): |
||||
FormatExternalFunction.create_patch(slither, patches, element['source_mapping']['filename'], "public", "external", int(function.parameters_src.split(':')[0]), int(function.returns_src.split(':')[0])) |
||||
Found = True |
||||
break |
||||
|
||||
@staticmethod |
||||
def create_patch(slither, patches, in_file, match_text, replace_text, modify_loc_start, modify_loc_end): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
old_str_of_interest_beyond_parameters = ')'.join(old_str_of_interest.split(')')[1:]) |
||||
s = old_str_of_interest_beyond_parameters.split('(') |
||||
if len(s) == 1: |
||||
account_for_return = 0 |
||||
else: |
||||
account_for_return = 1 |
||||
old_str_of_interest_beyond_parameters_before_modifier_return = old_str_of_interest_beyond_parameters.split('(')[0] |
||||
m = re.search("public", old_str_of_interest_beyond_parameters_before_modifier_return) |
||||
if m is None: |
||||
# No visibility specifier exists; public by default. |
||||
(new_str_of_interest, _) = re.subn(" ", " external ", old_str_of_interest_beyond_parameters_before_modifier_return, 1) |
||||
patches[in_file].append({ |
||||
"detector" : "external-function", |
||||
"start" : modify_loc_start + len(old_str_of_interest.split(')')[0]) + 1, |
||||
"end" : modify_loc_end - len('('.join(old_str_of_interest_beyond_parameters.split('(')[1:])) - account_for_return, |
||||
"old_string" : old_str_of_interest_beyond_parameters_before_modifier_return, |
||||
"new_string" : new_str_of_interest |
||||
}) |
||||
else: |
||||
(new_str_of_interest, _) = re.subn(match_text, replace_text, old_str_of_interest_beyond_parameters_before_modifier_return, 1) |
||||
patches[in_file].append({ |
||||
"detector" : "external-function", |
||||
"start" : modify_loc_start + len(old_str_of_interest.split(')')[0]) + 1 + m.span()[0], |
||||
"end" : modify_loc_end - len('('.join(old_str_of_interest_beyond_parameters.split('(')[1:])) - account_for_return, |
||||
"old_string" : old_str_of_interest_beyond_parameters_before_modifier_return, |
||||
"new_string" : new_str_of_interest |
||||
}) |
||||
|
||||
@staticmethod |
||||
def function_parameters_written(function): |
||||
for node in function.nodes: |
||||
if any (var.name == parameter.name for var in node.local_variables_written for parameter in function.parameters): |
||||
return True |
||||
return False |
||||
|
@ -0,0 +1,578 @@ |
||||
import re, sys |
||||
from slither.core.expressions.identifier import Identifier |
||||
from slither.core.cfg.node import Node |
||||
from slither.slithir.operations import NewContract |
||||
from slither.slithir.operations import Member |
||||
|
||||
class FormatNamingConvention: |
||||
|
||||
@staticmethod |
||||
def format(slither, patches, elements): |
||||
for element in elements: |
||||
if (element['target'] == "parameter"): |
||||
FormatNamingConvention.create_patch(slither, patches, element['target'], element['name'], element['function'], element['contract'], element['source_mapping']['filename'],element['source_mapping']['start'],(element['source_mapping']['start']+element['source_mapping']['length'])) |
||||
elif (element['target'] == "modifier" or element['target'] == "function" or element['target'] == "event" or element['target'] == "variable" or element['target'] == "variable_constant" or element['target'] == "enum" or element['target'] == "structure"): |
||||
FormatNamingConvention.create_patch(slither, patches, element['target'], element['name'], element['name'], element['contract'], element['source_mapping']['filename'],element['source_mapping']['start'],(element['source_mapping']['start']+element['source_mapping']['length'])) |
||||
else: |
||||
FormatNamingConvention.create_patch(slither, patches, element['target'], element['name'], element['name'], element['name'], element['source_mapping']['filename'],element['source_mapping']['start'],(element['source_mapping']['start']+element['source_mapping']['length'])) |
||||
|
||||
@staticmethod |
||||
def create_patch(slither, patches, _target, name, function_name, contract_name, in_file, modify_loc_start, modify_loc_end): |
||||
if _target == "contract": |
||||
FormatNamingConvention.create_patch_contract_definition(slither, patches, name, in_file, modify_loc_start, modify_loc_end) |
||||
FormatNamingConvention.create_patch_contract_uses(slither, patches, name, in_file) |
||||
elif _target == "structure": |
||||
FormatNamingConvention.create_patch_struct_definition(slither, patches, name, contract_name, in_file, modify_loc_start, modify_loc_end) |
||||
FormatNamingConvention.create_patch_struct_uses(slither, patches, name, contract_name, in_file) |
||||
elif _target == "event": |
||||
FormatNamingConvention.create_patch_event_definition(slither, patches, name, contract_name, in_file, modify_loc_start, modify_loc_end) |
||||
FormatNamingConvention.create_patch_event_calls(slither, patches, name, contract_name, in_file) |
||||
elif _target == "function": |
||||
if name != contract_name: |
||||
FormatNamingConvention.create_patch_function_definition(slither, patches, name, contract_name, in_file, modify_loc_start, modify_loc_end) |
||||
FormatNamingConvention.create_patch_function_calls(slither, patches, name, contract_name, in_file) |
||||
elif _target == "parameter": |
||||
FormatNamingConvention.create_patch_parameter_declaration(slither, patches, name, function_name, contract_name, in_file, modify_loc_start, modify_loc_end) |
||||
FormatNamingConvention.create_patch_parameter_uses(slither, patches, name, function_name, contract_name, in_file) |
||||
elif _target == "variable_constant" or _target == "variable": |
||||
FormatNamingConvention.create_patch_state_variable_declaration(slither, patches, _target, name, contract_name, in_file, modify_loc_start, modify_loc_end) |
||||
FormatNamingConvention.create_patch_state_variable_uses(slither, patches, _target, name, contract_name, in_file) |
||||
elif _target == "enum": |
||||
FormatNamingConvention.create_patch_enum_definition(slither, patches, name, contract_name, in_file, modify_loc_start, modify_loc_end) |
||||
FormatNamingConvention.create_patch_enum_uses(slither, patches, name, contract_name, in_file) |
||||
elif _target == "modifier": |
||||
FormatNamingConvention.create_patch_modifier_definition(slither, patches, name, contract_name, in_file, modify_loc_start, modify_loc_end) |
||||
FormatNamingConvention.create_patch_modifier_uses(slither, patches, name, contract_name, in_file) |
||||
else: |
||||
print("Unknown naming convention! " + _target) |
||||
sys.exit(-1) |
||||
|
||||
@staticmethod |
||||
def create_patch_contract_definition(slither, patches, name, in_file, modify_loc_start, modify_loc_end): |
||||
for contract in slither.contracts: |
||||
if contract.name == name: |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
m = re.match(r'(.*)'+"contract"+r'(.*)'+name, old_str_of_interest) |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_start+m.span()[1]] |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"contract"+r'(.*)'+name, r'\1'+"contract"+r'\2'+name.capitalize(), old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (contract definition)", |
||||
"start":modify_loc_start, |
||||
"end":modify_loc_start+m.span()[1], |
||||
"old_string":old_str_of_interest, |
||||
"new_string":new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find contract?!") |
||||
sys.exit(-1) |
||||
|
||||
@staticmethod |
||||
def create_patch_contract_uses(slither, patches, name, in_file): |
||||
for contract in slither.contracts: |
||||
if contract.name != name: |
||||
in_file_str = slither.source_code[in_file] |
||||
# Check state variables of contract type |
||||
# To-do: Deep-check aggregate types (struct and mapping) |
||||
svs = contract.variables |
||||
for sv in svs: |
||||
if (str(sv.type) == name): |
||||
old_str_of_interest = in_file_str[contract.get_source_var_declaration(sv.name)['start']:(contract.get_source_var_declaration(sv.name)['start']+contract.get_source_var_declaration(sv.name)['length'])] |
||||
(new_str_of_interest, num_repl) = re.subn(name, name.capitalize(),old_str_of_interest, 1) |
||||
patch = { |
||||
"detector" : "naming-convention (contract state variable)", |
||||
"start" : contract.get_source_var_declaration(sv.name)['start'], |
||||
"end" : contract.get_source_var_declaration(sv.name)['start'] + contract.get_source_var_declaration(sv.name)['length'], |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
# Check function+modifier locals+parameters+returns |
||||
# To-do: Deep-check aggregate types (struct and mapping) |
||||
fms = contract.functions + contract.modifiers |
||||
for fm in fms: |
||||
for v in fm.variables: |
||||
if (str(v.type) == name): |
||||
old_str_of_interest = in_file_str[fm.get_source_var_declaration(v.name)['start']:(fm.get_source_var_declaration(v.name)['start']+fm.get_source_var_declaration(v.name)['length'])] |
||||
(new_str_of_interest, num_repl) = re.subn(name, name.capitalize(),old_str_of_interest, 1) |
||||
patch = { |
||||
"detector" : "naming-convention (contract function variable)", |
||||
"start" : fm.get_source_var_declaration(v.name)['start'], |
||||
"end" : fm.get_source_var_declaration(v.name)['start'] + fm.get_source_var_declaration(v.name)['length'], |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
# Check "new" expressions for creation of contract objects |
||||
for function in contract.functions: |
||||
for node in function.nodes: |
||||
for ir in node.irs: |
||||
if isinstance(ir, NewContract) and ir.contract_name == name: |
||||
old_str_of_interest = in_file_str[node.source_mapping['start']:node.source_mapping['start'] + node.source_mapping['length']] |
||||
m = re.search("new"+r'(.*)'+name, old_str_of_interest) |
||||
old_str_of_interest = old_str_of_interest[m.span()[0]:] |
||||
(new_str_of_interest, num_repl) = re.subn("new"+r'(.*)'+name, "new"+r'\1'+name[0].upper()+name[1:], old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (contract new object)", |
||||
"start" : node.source_mapping['start'] + m.span()[0], |
||||
"end" : node.source_mapping['start'] + m.span()[1], |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find new object?!") |
||||
sys.exit(-1) |
||||
|
||||
else: |
||||
# Ignore contract definition |
||||
continue |
||||
|
||||
@staticmethod |
||||
def create_patch_modifier_definition(slither, patches, name, contract_name, in_file, modify_loc_start, modify_loc_end): |
||||
for contract in slither.contracts: |
||||
if contract.name == contract_name: |
||||
for modifier in contract.modifiers: |
||||
if modifier.name == name: |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
m = re.match(r'(.*)'+"modifier"+r'(.*)'+name, old_str_of_interest) |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_start+m.span()[1]] |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"modifier"+r'(.*)'+name, r'\1'+"modifier"+r'\2'+name[0].lower()+name[1:], old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (modifier definition)", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_start+m.span()[1], |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find modifier?!") |
||||
sys.exit(-1) |
||||
|
||||
@staticmethod |
||||
def create_patch_modifier_uses(slither, patches, name, contract_name, in_file): |
||||
for contract in slither.contracts: |
||||
if contract.name == contract_name: |
||||
for function in contract.functions: |
||||
for m in function.modifiers: |
||||
if (m.name == name): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[int(function.parameters_src.split(':')[0]):int(function.returns_src.split(':')[0])] |
||||
(new_str_of_interest, num_repl) = re.subn(name, name[0].lower()+name[1:],old_str_of_interest,1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (modifier uses)", |
||||
"start" : int(function.parameters_src.split(':')[0]), |
||||
"end" : int(function.returns_src.split(':')[0]), |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find modifier name?!") |
||||
sys.exit(-1) |
||||
|
||||
@staticmethod |
||||
def create_patch_function_definition(slither, patches, name, contract_name, in_file, modify_loc_start, modify_loc_end): |
||||
for contract in slither.contracts: |
||||
if contract.name == contract_name: |
||||
for function in contract.functions: |
||||
if function.name == name: |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
m = re.match(r'(.*)'+"function"+r'(.*)'+name, old_str_of_interest) |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_start+m.span()[1]] |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"function"+r'(.*)'+name, r'\1'+"function"+r'\2'+name[0].lower()+name[1:], old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (function definition)", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_start+m.span()[1], |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find function?!") |
||||
sys.exit(-1) |
||||
|
||||
@staticmethod |
||||
def create_patch_function_calls(slither, patches, name, contract_name, in_file): |
||||
for contract in slither.contracts: |
||||
for function in contract.functions: |
||||
for node in function.nodes: |
||||
for high_level_call in node.high_level_calls: |
||||
if (high_level_call[0].name == contract_name and high_level_call[1].name == name): |
||||
for external_call in node.external_calls_as_expressions: |
||||
called_function = str(external_call.called).split('.')[-1] |
||||
if called_function == high_level_call[1].name: |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[int(external_call.source_mapping['start']):int(external_call.source_mapping['start'])+int(external_call.source_mapping['length'])] |
||||
called_function_name = old_str_of_interest.split('.')[-1] |
||||
fixed_function_name = called_function_name[0].lower() + called_function_name[1:] |
||||
new_string = '.'.join(old_str_of_interest.split('.')[:-1]) + '.' + fixed_function_name |
||||
patch = { |
||||
"detector" : "naming-convention (function calls)", |
||||
"start" : external_call.source_mapping['start'], |
||||
"end" : int(external_call.source_mapping['start']) + int(external_call.source_mapping['length']), |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_string |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
for internal_call in node.internal_calls_as_expressions: |
||||
if (str(internal_call.called) == name): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[int(internal_call.source_mapping['start']):int(internal_call.source_mapping['start'])+int(internal_call.source_mapping['length'])] |
||||
patch = { |
||||
"detector" : "naming-convention (function calls)", |
||||
"start" : internal_call.source_mapping['start'], |
||||
"end" : int(internal_call.source_mapping['start']) + int(internal_call.source_mapping['length']), |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : old_str_of_interest[0].lower()+old_str_of_interest[1:] |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
|
||||
@staticmethod |
||||
def create_patch_event_definition(slither, patches, name, contract_name, in_file, modify_loc_start, modify_loc_end): |
||||
for contract in slither.contracts: |
||||
if contract.name == contract_name: |
||||
for event in contract.events: |
||||
if event.full_name == name: |
||||
event_name = name.split('(')[0] |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"event"+r'(.*)'+event_name, r'\1'+"event"+r'\2'+event_name[0].capitalize()+event_name[1:], old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (event definition)", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_end, |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find event?!") |
||||
sys.exit(-1) |
||||
|
||||
@staticmethod |
||||
def create_patch_event_calls(slither, patches, name, contract_name, in_file): |
||||
event_name = name.split('(')[0] |
||||
for contract in slither.contracts: |
||||
if (contract.name == contract_name): |
||||
for function in contract.functions: |
||||
for node in function.nodes: |
||||
for call in node.internal_calls_as_expressions: |
||||
if (str(call.called) == event_name): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[int(call.source_mapping['start']):int(call.source_mapping['start'])+int(call.source_mapping['length'])] |
||||
patch = { |
||||
"detector" : "naming-convention (event calls)", |
||||
"start" : call.source_mapping['start'], |
||||
"end" : int(call.source_mapping['start']) + int(call.source_mapping['length']), |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : old_str_of_interest[0].capitalize()+old_str_of_interest[1:] |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
|
||||
@staticmethod |
||||
def create_patch_parameter_declaration(slither, patches, name, function_name, contract_name, in_file, modify_loc_start, modify_loc_end): |
||||
for contract in slither.contracts: |
||||
if contract.name == contract_name: |
||||
for function in contract.functions: |
||||
if function.name == function_name: |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
if(name[0] == '_'): |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+name[0]+name[1].upper()+name[2:]+r'\2', old_str_of_interest, 1) |
||||
else: |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+'_'+name[0].upper()+name[1:]+r'\2', old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (parameter declaration)", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_end, |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find parameter?!") |
||||
sys.exit(-1) |
||||
|
||||
@staticmethod |
||||
def create_patch_parameter_uses(slither, patches, name, function_name, contract_name, in_file): |
||||
for contract in slither.contracts: |
||||
if (contract.name == contract_name): |
||||
for function in contract.functions: |
||||
if (function.name == function_name): |
||||
in_file_str = slither.source_code[in_file] |
||||
for node in function.nodes: |
||||
vars = node._expression_vars_written + node._expression_vars_read |
||||
for v in vars: |
||||
if isinstance(v, Identifier) and str(v) == name and [str(lv) for lv in (node._local_vars_read+node._local_vars_written) if str(lv) == name]: |
||||
modify_loc_start = int(v.source_mapping['start']) |
||||
modify_loc_end = int(v.source_mapping['start']) + int(v.source_mapping['length']) |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
if(name[0] == '_'): |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+name[0]+name[1].upper()+name[2:]+r'\2', old_str_of_interest, 1) |
||||
else: |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+'_'+name[0].upper()+name[1:]+r'\2', old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (parameter uses)", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_end, |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find parameter?!") |
||||
sys.exit(-1) |
||||
# Process function parameters passed to modifiers |
||||
for modifier in function._expression_modifiers: |
||||
for arg in modifier.arguments: |
||||
if str(arg) == name: |
||||
old_str_of_interest = in_file_str[modifier.source_mapping['start']:modifier.source_mapping['start']+modifier.source_mapping['length']] |
||||
old_str_of_interest_beyond_modifier_name = old_str_of_interest.split('(')[1] |
||||
if(name[0] == '_'): |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+name[0]+name[1].upper()+name[2:]+r'\2', old_str_of_interest_beyond_modifier_name, 1) |
||||
else: |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+'_'+name[0].upper()+name[1:]+r'\2', old_str_of_interest_beyond_modifier_name, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (parameter uses)", |
||||
"start" : modifier.source_mapping['start'] + len(old_str_of_interest.split('(')[0]) + 1, |
||||
"end" : modifier.source_mapping['start'] + modifier.source_mapping['length'], |
||||
"old_string" : old_str_of_interest_beyond_modifier_name, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find parameter?!") |
||||
sys.exit(-1) |
||||
|
||||
@staticmethod |
||||
def create_patch_state_variable_declaration(slither, patches, _target, name, contract_name, in_file, modify_loc_start, modify_loc_end): |
||||
for contract in slither.contracts: |
||||
if (contract.name == contract_name): |
||||
for var in contract.state_variables: |
||||
if (var.name == name): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
m = re.search(name, old_str_of_interest) |
||||
if (_target == "variable_constant"): |
||||
new_string = old_str_of_interest[m.span()[0]:m.span()[1]].upper() |
||||
else: |
||||
new_string = old_str_of_interest[m.span()[0]:m.span()[1]] |
||||
new_string = new_string[0].lower()+new_string[1:] |
||||
patch = { |
||||
"detector" : "naming-convention (state variable declaration)", |
||||
"start" : modify_loc_start+m.span()[0], |
||||
"end" : modify_loc_start+m.span()[1], |
||||
"old_string" : old_str_of_interest[m.span()[0]:m.span()[1]], |
||||
"new_string" : new_string |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
|
||||
@staticmethod |
||||
def create_patch_state_variable_uses(slither, patches, _target, name, contract_name, in_file): |
||||
# To-do: Check cross-contract state variable uses |
||||
for contract in slither.contracts: |
||||
if (contract.name == contract_name): |
||||
fms = contract.functions + contract.modifiers |
||||
for fm in fms: |
||||
for node in fm.nodes: |
||||
vars = node._expression_vars_written + node._expression_vars_read |
||||
for v in vars: |
||||
if isinstance(v, Identifier) and str(v) == name and [str(sv) for sv in (node._state_vars_read+node._state_vars_written) if str(sv) == name]: |
||||
modify_loc_start = int(v.source_mapping['start']) |
||||
modify_loc_end = int(v.source_mapping['start']) + int(v.source_mapping['length']) |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
if (_target == "variable_constant"): |
||||
new_str_of_interest = old_str_of_interest.upper() |
||||
else: |
||||
new_str_of_interest = old_str_of_interest |
||||
new_str_of_interest = new_str_of_interest[0].lower()+new_str_of_interest[1:] |
||||
patch = { |
||||
"detector" : "naming-convention (state variable uses)", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_end, |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
|
||||
@staticmethod |
||||
def create_patch_enum_definition(slither, patches, name, contract_name, in_file, modify_loc_start, modify_loc_end): |
||||
for contract in slither.contracts: |
||||
if (contract.name == contract_name): |
||||
for enum in contract.enums: |
||||
if (enum.name == name): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"enum"+r'(.*)'+name, r'\1'+"enum"+r'\2'+name[0].capitalize()+name[1:], old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (enum definition)", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_end, |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find enum?!") |
||||
sys.exit(-1) |
||||
|
||||
@staticmethod |
||||
def create_patch_enum_uses(slither, patches, name, contract_name, in_file): |
||||
for contract in slither.contracts: |
||||
if contract.name == contract_name: |
||||
in_file_str = slither.source_code[in_file] |
||||
# Check state variable declarations of enum type |
||||
# To-do: Deep-check aggregate types (struct and mapping) |
||||
svs = contract.variables |
||||
for sv in svs: |
||||
if (str(sv.type) == contract_name + "." + name): |
||||
old_str_of_interest = in_file_str[contract.get_source_var_declaration(sv.name)['start']:(contract.get_source_var_declaration(sv.name)['start']+contract.get_source_var_declaration(sv.name)['length'])] |
||||
(new_str_of_interest, num_repl) = re.subn(name, name.capitalize(),old_str_of_interest, 1) |
||||
patch = { |
||||
"detector" : "naming-convention (enum use)", |
||||
"start" : contract.get_source_var_declaration(sv.name)['start'], |
||||
"end" : contract.get_source_var_declaration(sv.name)['start'] + contract.get_source_var_declaration(sv.name)['length'], |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
# Check function+modifier locals+parameters+returns |
||||
# To-do: Deep-check aggregate types (struct and mapping) |
||||
fms = contract.functions + contract.modifiers |
||||
for fm in fms: |
||||
# Enum declarations |
||||
for v in fm.variables: |
||||
if (str(v.type) == contract_name + "." + name): |
||||
old_str_of_interest = in_file_str[fm.get_source_var_declaration(v.name)['start']:(fm.get_source_var_declaration(v.name)['start']+fm.get_source_var_declaration(v.name)['length'])] |
||||
(new_str_of_interest, num_repl) = re.subn(name, name.capitalize(),old_str_of_interest, 1) |
||||
patch = { |
||||
"detector" : "naming-convention (enum use)", |
||||
"start" : fm.get_source_var_declaration(v.name)['start'], |
||||
"end" : fm.get_source_var_declaration(v.name)['start'] + fm.get_source_var_declaration(v.name)['length'], |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
# Capture enum uses such as "num = numbers.ONE;" |
||||
for function in contract.functions: |
||||
for node in function.nodes: |
||||
for ir in node.irs: |
||||
if isinstance(ir, Member): |
||||
if str(ir.variable_left) == name: |
||||
old_str_of_interest = in_file_str[node.source_mapping['start']:(node.source_mapping['start']+node.source_mapping['length'])].split('=')[1] |
||||
m = re.search(r'(.*)'+name, old_str_of_interest) |
||||
old_str_of_interest = old_str_of_interest[m.span()[0]:] |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name, r'\1'+name[0].upper()+name[1:], old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (enum use)", |
||||
"start" : node.source_mapping['start'] + len(in_file_str[node.source_mapping['start']:(node.source_mapping['start']+node.source_mapping['length'])].split('=')[0]) + 1 + m.span()[0], |
||||
"end" : node.source_mapping['start'] + len(in_file_str[node.source_mapping['star\ |
||||
t']:(node.source_mapping['start']+node.source_mapping['length'])].split('=')[0]) + 1 + m.span()[0] + len(old_str_of_interest), |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find new object?!") |
||||
sys.exit(-1) |
||||
# To-do: Check any other place/way where enum type is used |
||||
|
||||
@staticmethod |
||||
def create_patch_struct_definition(slither, patches, name, contract_name, in_file, modify_loc_start, modify_loc_end): |
||||
for contract in slither.contracts: |
||||
if (contract.name == contract_name): |
||||
for struct in contract.structures: |
||||
if (struct.name == name): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"struct"+r'(.*)'+name, r'\1'+"struct"+r'\2'+name[0].capitalize()+name[1:], old_str_of_interest, 1) |
||||
if num_repl != 0: |
||||
patch = { |
||||
"detector" : "naming-convention (struct definition)", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_end, |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
else: |
||||
print("Error: Could not find struct?!") |
||||
sys.exit(-1) |
||||
|
||||
@staticmethod |
||||
def create_patch_struct_uses(slither, patches, name, contract_name, in_file): |
||||
for contract in slither.contracts: |
||||
in_file_str = slither.source_code[in_file] |
||||
# Check state variables of struct type |
||||
# To-do: Deep-check aggregate types (struct and mapping) |
||||
svs = contract.variables |
||||
for sv in svs: |
||||
if (str(sv.type) == contract_name + "." + name): |
||||
old_str_of_interest = in_file_str[contract.get_source_var_declaration(sv.name)['start']:(contract.get_source_var_declaration(sv.name)['start']+contract.get_source_var_declaration(sv.name)['length'])] |
||||
(new_str_of_interest, num_repl) = re.subn(name, name.capitalize(),old_str_of_interest, 1) |
||||
patch = { |
||||
"detector" : "naming-convention (struct use)", |
||||
"start" : contract.get_source_var_declaration(sv.name)['start'], |
||||
"end" : contract.get_source_var_declaration(sv.name)['start'] + contract.get_source_var_declaration(sv.name)['length'], |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
# Check function+modifier locals+parameters+returns |
||||
# To-do: Deep-check aggregate types (struct and mapping) |
||||
fms = contract.functions + contract.modifiers |
||||
for fm in fms: |
||||
for v in fm.variables: |
||||
if (str(v.type) == contract_name + "." + name): |
||||
old_str_of_interest = in_file_str[fm.get_source_var_declaration(v.name)['start']:(fm.get_source_var_declaration(v.name)['start']+fm.get_source_var_declaration(v.name)['length'])] |
||||
(new_str_of_interest, num_repl) = re.subn(name, name.capitalize(),old_str_of_interest, 1) |
||||
patch = { |
||||
"detector" : "naming-convention (struct use)", |
||||
"start" : fm.get_source_var_declaration(v.name)['start'], |
||||
"end" : fm.get_source_var_declaration(v.name)['start'] + fm.get_source_var_declaration(v.name)['length'], |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : new_str_of_interest |
||||
} |
||||
if not patch in patches[in_file]: |
||||
patches[in_file].append(patch) |
||||
# To-do: Check any other place/way where struct type is used (e.g. typecast) |
@ -0,0 +1,69 @@ |
||||
import re |
||||
|
||||
class FormatPragma: |
||||
|
||||
# Indicates the recommended versions for replacement |
||||
REPLACEMENT_VERSIONS = ["0.4.25", "0.5.3"] |
||||
|
||||
# group: |
||||
# 0: ^ > >= < <= (optional) |
||||
# 1: ' ' (optional) |
||||
# 2: version number |
||||
# 3: version number |
||||
# 4: version number |
||||
PATTERN = re.compile('(\^|>|>=|<|<=)?([ ]+)?(\d+)\.(\d+)\.(\d+)') |
||||
|
||||
@staticmethod |
||||
def format(slither, patches, elements): |
||||
versions_used = [] |
||||
for element in elements: |
||||
versions_used.append(element['expression']) |
||||
solc_version_replace = FormatPragma.analyse_versions(versions_used) |
||||
for element in elements: |
||||
FormatPragma.create_patch(slither, patches, element['source_mapping']['filename'], solc_version_replace, element['source_mapping']['start'], element['source_mapping']['start'] + element['source_mapping']['length']) |
||||
|
||||
@staticmethod |
||||
def analyse_versions(used_solc_versions): |
||||
replace_solc_versions = list() |
||||
for version in used_solc_versions: |
||||
replace_solc_versions.append(FormatPragma.determine_solc_version_replacement(version)) |
||||
if not all(version == replace_solc_versions[0] for version in replace_solc_versions): |
||||
print("Multiple incompatible versions!") |
||||
sys.exit(-1) |
||||
else: |
||||
return replace_solc_versions[0] |
||||
|
||||
@staticmethod |
||||
def determine_solc_version_replacement(used_solc_version): |
||||
versions = FormatPragma.PATTERN.findall(used_solc_version) |
||||
if len(versions) == 1: |
||||
version = versions[0] |
||||
minor_version = '.'.join(version[2:])[2] |
||||
if minor_version == '4': |
||||
return "pragma solidity " + FormatPragma.REPLACEMENT_VERSIONS[0] + ';' |
||||
elif minor_version == '5': |
||||
return "pragma solidity " + FormatPragma.REPLACEMENT_VERSIONS[1] + ';' |
||||
else: |
||||
print("Unknown version!") |
||||
sys.exit(-1) |
||||
elif len(versions) == 2: |
||||
version_left = versions[0] |
||||
version_right = versions[1] |
||||
minor_version_left = '.'.join(version_left[2:])[2] |
||||
minor_version_right = '.'.join(version_right[2:])[2] |
||||
if minor_version_right == '4': |
||||
return "pragma solidity " + FormatPragma.REPLACEMENT_VERSIONS[0] + ';' |
||||
elif minor_version_right in ['5','6']: |
||||
return "pragma solidity " + FormatPragma.REPLACEMENT_VERSIONS[1] + ';' |
||||
|
||||
@staticmethod |
||||
def create_patch(slither, patches, in_file, pragma, modify_loc_start, modify_loc_end): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
patches[in_file].append({ |
||||
"detector" : "pragma", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_end, |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : pragma |
||||
}) |
@ -0,0 +1,55 @@ |
||||
import re |
||||
|
||||
class FormatSolcVersion: |
||||
|
||||
# Indicates the recommended versions for replacement |
||||
REPLACEMENT_VERSIONS = ["0.4.25", "0.5.3"] |
||||
|
||||
# group: |
||||
# 0: ^ > >= < <= (optional) |
||||
# 1: ' ' (optional) |
||||
# 2: version number |
||||
# 3: version number |
||||
# 4: version number |
||||
PATTERN = re.compile('(\^|>|>=|<|<=)?([ ]+)?(\d+)\.(\d+)\.(\d+)') |
||||
|
||||
@staticmethod |
||||
def format(slither, patches, elements): |
||||
for element in elements: |
||||
solc_version_replace = FormatSolcVersion.determine_solc_version_replacement(element['expression']) |
||||
FormatSolcVersion.create_patch(slither, patches, element['source_mapping']['filename'], solc_version_replace, element['source_mapping']['start'], element['source_mapping']['start'] + element['source_mapping']['length']) |
||||
|
||||
@staticmethod |
||||
def determine_solc_version_replacement(used_solc_version): |
||||
versions = FormatSolcVersion.PATTERN.findall(used_solc_version) |
||||
if len(versions) == 1: |
||||
version = versions[0] |
||||
minor_version = '.'.join(version[2:])[2] |
||||
if minor_version == '4': |
||||
return "pragma solidity " + FormatSolcVersion.REPLACEMENT_VERSIONS[0] + ';' |
||||
elif minor_version == '5': |
||||
return "pragma solidity " + FormatSolcVersion.REPLACEMENT_VERSIONS[1] + ';' |
||||
else: |
||||
print("Unknown version!") |
||||
sys.exit(-1) |
||||
elif len(versions) == 2: |
||||
version_left = versions[0] |
||||
version_right = versions[1] |
||||
minor_version_left = '.'.join(version_left[2:])[2] |
||||
minor_version_right = '.'.join(version_right[2:])[2] |
||||
if minor_version_right == '4': |
||||
return "pragma solidity " + FormatSolcVersion.REPLACEMENT_VERSIONS[0] + ';' |
||||
elif minor_version_right in ['5','6']: |
||||
return "pragma solidity " + FormatSolcVersion.REPLACEMENT_VERSIONS[1] + ';' |
||||
|
||||
@staticmethod |
||||
def create_patch(slither, patches, in_file, solc_version, modify_loc_start, modify_loc_end): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
patches[in_file].append({ |
||||
"detector" : "solc-version", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_end, |
||||
"old_string" : old_str_of_interest, |
||||
"new_string" : solc_version |
||||
}) |
@ -0,0 +1,19 @@ |
||||
class FormatUnusedState: |
||||
|
||||
@staticmethod |
||||
def format(slither, patches, elements): |
||||
for element in elements: |
||||
FormatUnusedState.create_patch(slither, patches, element['source_mapping']['filename'], element['source_mapping']['start']) |
||||
|
||||
@staticmethod |
||||
def create_patch(slither, patches, in_file, modify_loc_start): |
||||
in_file_str = slither.source_code[in_file] |
||||
old_str_of_interest = in_file_str[modify_loc_start:] |
||||
patches[in_file].append({ |
||||
"detector" : "unused-state", |
||||
"start" : modify_loc_start, |
||||
"end" : modify_loc_start + len(old_str_of_interest.partition(';')[0]) + 1, |
||||
"old_string" : old_str_of_interest.partition(';')[0] + old_str_of_interest.partition(';')[1], |
||||
"new_string" : "" |
||||
}) |
||||
|
@ -0,0 +1,132 @@ |
||||
import sys, re |
||||
from collections import defaultdict |
||||
from slither.detectors.variables.unused_state_variables import UnusedStateVars |
||||
from slither.detectors.attributes.incorrect_solc import IncorrectSolc |
||||
from slither.detectors.attributes.constant_pragma import ConstantPragma |
||||
from slither.detectors.naming_convention.naming_convention import NamingConvention |
||||
from slither.detectors.functions.external_function import ExternalFunction |
||||
from slither.detectors.variables.possible_const_state_variables import ConstCandidateStateVars |
||||
from slither.detectors.attributes.const_functions import ConstantFunctions |
||||
from slither.slithir.operations import InternalCall |
||||
from slither.core.expressions.call_expression import CallExpression |
||||
from slither.core.expressions.expression import Expression |
||||
from slither.core.expressions.identifier import Identifier |
||||
from slither_format.format_unused_state import FormatUnusedState |
||||
from slither_format.format_solc_version import FormatSolcVersion |
||||
from slither_format.format_pragma import FormatPragma |
||||
from slither_format.format_naming_convention import FormatNamingConvention |
||||
from slither_format.format_external_function import FormatExternalFunction |
||||
from slither_format.format_constable_states import FormatConstableStates |
||||
from slither_format.format_constant_function import FormatConstantFunction |
||||
|
||||
all_detectors = { |
||||
'unused-state': UnusedStateVars, |
||||
'solc-version': IncorrectSolc, |
||||
'pragma': ConstantPragma, |
||||
'naming-convention': NamingConvention, |
||||
'external-function': ExternalFunction, |
||||
'constable-states' : ConstCandidateStateVars, |
||||
'constant-function': ConstantFunctions |
||||
} |
||||
|
||||
def slither_format(args, slither): |
||||
patches = defaultdict(list) |
||||
detectors_to_run = choose_detectors(args) |
||||
for detector in detectors_to_run: |
||||
slither.register_detector(detector) |
||||
results = [] |
||||
detector_results = slither.run_detectors() |
||||
detector_results = [x for x in detector_results if x] # remove empty results |
||||
detector_results = [item for sublist in detector_results for item in sublist] # flatten |
||||
results.extend(detector_results) |
||||
number_of_slither_results = get_number_of_slither_results(detector_results) |
||||
apply_detector_results(slither, patches, detector_results) |
||||
sort_patches(patches) |
||||
if args.verbose: |
||||
print("Number of Slither results: " + str(number_of_slither_results)) |
||||
print_patches(patches) |
||||
apply_patches(slither, patches) |
||||
|
||||
def sort_patches(patches): |
||||
for file in patches: |
||||
n = len(patches[file]) |
||||
for i in range(n): |
||||
for j in range (0,n-i-1): |
||||
if int(patches[file][j]['start']) >= int(patches[file][j+1]['end']): |
||||
temp = patches[file][j+1] |
||||
patches[file][j+1] = patches[file][j] |
||||
patches[file][j] = temp |
||||
|
||||
def apply_patches(slither, patches): |
||||
for file in patches: |
||||
_in_file = file |
||||
in_file_str = slither.source_code[_in_file] |
||||
out_file_str = "" |
||||
for i in range(len(patches[file])): |
||||
if i != 0: |
||||
out_file_str += in_file_str[int(patches[file][i-1]['end']):int(patches[file][i]['start'])] |
||||
else: |
||||
out_file_str += in_file_str[:int(patches[file][i]['start'])] |
||||
out_file_str += patches[file][i]['new_string'] |
||||
out_file_str += in_file_str[int(patches[file][i]['end']):] |
||||
out_file = open(_in_file+".format",'w') |
||||
out_file.write(out_file_str) |
||||
out_file.close() |
||||
|
||||
def print_patches(patches): |
||||
number_of_patches = 0 |
||||
for file in patches: |
||||
number_of_patches += len(patches[file]) |
||||
print("Number of patches: " + str(number_of_patches)) |
||||
for file in patches: |
||||
print("Patch file: " + file) |
||||
for patch in patches[file]: |
||||
print("Detector: " + patch['detector']) |
||||
print("Old string: " + patch['old_string'].replace("\n","")) |
||||
print("New string: " + patch['new_string'].replace("\n","")) |
||||
print("Location start: " + str(patch['start'])) |
||||
print("Location end: " + str(patch['end'])) |
||||
|
||||
def choose_detectors(args): |
||||
# If detectors are specified, run only these ones |
||||
detectors_to_run = [] |
||||
if args.detectors_to_run == 'all': |
||||
for d in all_detectors: |
||||
detectors_to_run.append(all_detectors[d]) |
||||
else: |
||||
for d in args.detectors_to_run.split(','): |
||||
if d in all_detectors: |
||||
detectors_to_run.append(all_detectors[d]) |
||||
else: |
||||
raise Exception('Error: {} is not a detector'.format(d)) |
||||
return detectors_to_run |
||||
|
||||
def apply_detector_results(slither, patches, detector_results): |
||||
for result in detector_results: |
||||
if result['check'] == 'unused-state': |
||||
FormatUnusedState.format(slither, patches, result['elements']) |
||||
elif result['check'] == 'solc-version': |
||||
FormatSolcVersion.format(slither, patches, result['elements']) |
||||
elif result['check'] == 'pragma': |
||||
FormatPragma.format(slither, patches, result['elements']) |
||||
elif result['check'] == 'naming-convention': |
||||
FormatNamingConvention.format(slither, patches, result['elements']) |
||||
elif result['check'] == 'external-function': |
||||
FormatExternalFunction.format(slither, patches, result['elements']) |
||||
elif result['check'] == 'constable-states': |
||||
FormatConstableStates.format(slither, patches, result['elements']) |
||||
elif result['check'] == 'constant-function': |
||||
FormatConstantFunction.format(slither, patches, result['elements']) |
||||
else: |
||||
print("Not Supported Yet.") |
||||
sys.exit(-1) |
||||
|
||||
def get_number_of_slither_results (detector_results): |
||||
number_of_slither_results = 0 |
||||
for result in detector_results: |
||||
for elem in result['elements']: |
||||
if (result['check'] == 'constant-function' and elem['type'] != "function"): |
||||
continue |
||||
number_of_slither_results += 1 |
||||
return number_of_slither_results |
||||
|
@ -0,0 +1 @@ |
||||
*.patch.* |
@ -0,0 +1,332 @@ |
||||
pragma solidity ^0.4.10; |
||||
|
||||
contract GasToken2 { |
||||
////////////////////////////////////////////////////////////////////////// |
||||
// RLP.sol |
||||
// Due to some unexplained bug, we get a slightly different bytecode if |
||||
// we use an import, and are then unable to verify the code in Etherscan |
||||
////////////////////////////////////////////////////////////////////////// |
||||
|
||||
uint256 constant ADDRESS_BYTES = 20; |
||||
uint256 constant MAX_SINGLE_BYTE = 128; |
||||
uint256 constant MAX_NONCE = 256**9 - 1; |
||||
|
||||
// count number of bytes required to represent an unsigned integer |
||||
function count_bytes(uint256 n) constant internal returns (uint256 c) { |
||||
uint i = 0; |
||||
uint mask = 1; |
||||
while (n >= mask) { |
||||
i += 1; |
||||
mask *= 256; |
||||
} |
||||
|
||||
return i; |
||||
} |
||||
|
||||
function mk_contract_address(address a, uint256 n) constant internal returns (address rlp) { |
||||
/* |
||||
* make sure the RLP encoding fits in one word: |
||||
* total_length 1 byte |
||||
* address_length 1 byte |
||||
* address 20 bytes |
||||
* nonce_length 1 byte (or 0) |
||||
* nonce 1-9 bytes |
||||
* ========== |
||||
* 24-32 bytes |
||||
*/ |
||||
require(n <= MAX_NONCE); |
||||
|
||||
// number of bytes required to write down the nonce |
||||
uint256 nonce_bytes; |
||||
// length in bytes of the RLP encoding of the nonce |
||||
uint256 nonce_rlp_len; |
||||
|
||||
if (0 < n && n < MAX_SINGLE_BYTE) { |
||||
// nonce fits in a single byte |
||||
// RLP(nonce) = nonce |
||||
nonce_bytes = 1; |
||||
nonce_rlp_len = 1; |
||||
} else { |
||||
// RLP(nonce) = [num_bytes_in_nonce nonce] |
||||
nonce_bytes = count_bytes(n); |
||||
nonce_rlp_len = nonce_bytes + 1; |
||||
} |
||||
|
||||
// [address_length(1) address(20) nonce_length(0 or 1) nonce(1-9)] |
||||
uint256 tot_bytes = 1 + ADDRESS_BYTES + nonce_rlp_len; |
||||
|
||||
// concatenate all parts of the RLP encoding in the leading bytes of |
||||
// one 32-byte word |
||||
uint256 word = ((192 + tot_bytes) * 256**31) + |
||||
((128 + ADDRESS_BYTES) * 256**30) + |
||||
(uint256(a) * 256**10); |
||||
|
||||
if (0 < n && n < MAX_SINGLE_BYTE) { |
||||
word += n * 256**9; |
||||
} else { |
||||
word += (128 + nonce_bytes) * 256**9; |
||||
word += n * 256**(9 - nonce_bytes); |
||||
} |
||||
|
||||
uint256 hash; |
||||
|
||||
assembly { |
||||
let mem_start := mload(0x40) // get a pointer to free memory |
||||
mstore(0x40, add(mem_start, 0x20)) // update the pointer |
||||
|
||||
mstore(mem_start, word) // store the rlp encoding |
||||
hash := sha3(mem_start, |
||||
add(tot_bytes, 1)) // hash the rlp encoding |
||||
} |
||||
|
||||
// interpret hash as address (20 least significant bytes) |
||||
return address(hash); |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////////////////////// |
||||
// Generic ERC20 |
||||
////////////////////////////////////////////////////////////////////////// |
||||
|
||||
// owner -> amount |
||||
mapping(address => uint256) s_balances; |
||||
// owner -> spender -> max amount |
||||
mapping(address => mapping(address => uint256)) s_allowances; |
||||
|
||||
event Transfer(address indexed from, address indexed to, uint256 value); |
||||
|
||||
event Approval(address indexed owner, address indexed spender, uint256 value); |
||||
|
||||
// Spec: Get the account balance of another account with address `owner` |
||||
function balanceOf(address owner) public constant returns (uint256 balance) { |
||||
return s_balances[owner]; |
||||
} |
||||
|
||||
function internalTransfer(address from, address to, uint256 value) internal returns (bool success) { |
||||
if (value <= s_balances[from]) { |
||||
s_balances[from] -= value; |
||||
s_balances[to] += value; |
||||
Transfer(from, to, value); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
// Spec: Send `value` amount of tokens to address `to` |
||||
function transfer(address to, uint256 value) public returns (bool success) { |
||||
address from = msg.sender; |
||||
return internalTransfer(from, to, value); |
||||
} |
||||
|
||||
// Spec: Send `value` amount of tokens from address `from` to address `to` |
||||
function transferFrom(address from, address to, uint256 value) public returns (bool success) { |
||||
address spender = msg.sender; |
||||
if(value <= s_allowances[from][spender] && internalTransfer(from, to, value)) { |
||||
s_allowances[from][spender] -= value; |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
// Spec: Allow `spender` to withdraw from your account, multiple times, up |
||||
// to the `value` amount. If this function is called again it overwrites the |
||||
// current allowance with `value`. |
||||
function approve(address spender, uint256 value) public returns (bool success) { |
||||
address owner = msg.sender; |
||||
if (value != 0 && s_allowances[owner][spender] != 0) { |
||||
return false; |
||||
} |
||||
s_allowances[owner][spender] = value; |
||||
Approval(owner, spender, value); |
||||
return true; |
||||
} |
||||
|
||||
// Spec: Returns the `amount` which `spender` is still allowed to withdraw |
||||
// from `owner`. |
||||
// What if the allowance is higher than the balance of the `owner`? |
||||
// Callers should be careful to use min(allowance, balanceOf) to make sure |
||||
// that the allowance is actually present in the account! |
||||
function allowance(address owner, address spender) public constant returns (uint256 remaining) { |
||||
return s_allowances[owner][spender]; |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////////////////////// |
||||
// GasToken specifics |
||||
////////////////////////////////////////////////////////////////////////// |
||||
|
||||
uint8 constant public decimals = 2; |
||||
string constant public name = "Gastoken.io"; |
||||
string constant public symbol = "GST2"; |
||||
|
||||
// We build a queue of nonces at which child contracts are stored. s_head is |
||||
// the nonce at the head of the queue, s_tail is the nonce behind the tail |
||||
// of the queue. The queue grows at the head and shrinks from the tail. |
||||
// Note that when and only when a contract CREATEs another contract, the |
||||
// creating contract's nonce is incremented. |
||||
// The first child contract is created with nonce == 1, the second child |
||||
// contract is created with nonce == 2, and so on... |
||||
// For example, if there are child contracts at nonces [2,3,4], |
||||
// then s_head == 4 and s_tail == 1. If there are no child contracts, |
||||
// s_head == s_tail. |
||||
uint256 s_head; |
||||
uint256 s_tail; |
||||
|
||||
// totalSupply gives the number of tokens currently in existence |
||||
// Each token corresponds to one child contract that can be SELFDESTRUCTed |
||||
// for a gas refund. |
||||
function totalSupply() public constant returns (uint256 supply) { |
||||
return s_head - s_tail; |
||||
} |
||||
|
||||
// Creates a child contract that can only be destroyed by this contract. |
||||
function makeChild() internal returns (address addr) { |
||||
assembly { |
||||
// EVM assembler of runtime portion of child contract: |
||||
// ;; Pseudocode: if (msg.sender != 0x0000000000b3f879cb30fe243b4dfee438691c04) { throw; } |
||||
// ;; suicide(msg.sender) |
||||
// PUSH15 0xb3f879cb30fe243b4dfee438691c04 ;; hardcoded address of this contract |
||||
// CALLER |
||||
// XOR |
||||
// PC |
||||
// JUMPI |
||||
// CALLER |
||||
// SELFDESTRUCT |
||||
// Or in binary: 6eb3f879cb30fe243b4dfee438691c043318585733ff |
||||
// Since the binary is so short (22 bytes), we can get away |
||||
// with a very simple initcode: |
||||
// PUSH22 0x6eb3f879cb30fe243b4dfee438691c043318585733ff |
||||
// PUSH1 0 |
||||
// MSTORE ;; at this point, memory locations mem[10] through |
||||
// ;; mem[31] contain the runtime portion of the child |
||||
// ;; contract. all that's left to do is to RETURN this |
||||
// ;; chunk of memory. |
||||
// PUSH1 22 ;; length |
||||
// PUSH1 10 ;; offset |
||||
// RETURN |
||||
// Or in binary: 756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3 |
||||
// Almost done! All we have to do is put this short (31 bytes) blob into |
||||
// memory and call CREATE with the appropriate offsets. |
||||
let solidity_free_mem_ptr := mload(0x40) |
||||
mstore(solidity_free_mem_ptr, 0x00756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3) |
||||
addr := create(0, add(solidity_free_mem_ptr, 1), 31) |
||||
} |
||||
} |
||||
|
||||
// Mints `value` new sub-tokens (e.g. cents, pennies, ...) by creating `value` |
||||
// new child contracts. The minted tokens are owned by the caller of this |
||||
// function. |
||||
function mint(uint256 value) public { |
||||
for (uint256 i = 0; i < value; i++) { |
||||
makeChild(); |
||||
} |
||||
s_head += value; |
||||
s_balances[msg.sender] += value; |
||||
} |
||||
|
||||
// Destroys `value` child contracts and updates s_tail. |
||||
// |
||||
// This function is affected by an issue in solc: https://github.com/ethereum/solidity/issues/2999 |
||||
// The `mk_contract_address(this, i).call();` doesn't forward all available gas, but only GAS - 25710. |
||||
// As a result, when this line is executed with e.g. 30000 gas, the callee will have less than 5000 gas |
||||
// available and its SELFDESTRUCT operation will fail leading to no gas refund occurring. |
||||
// The remaining ~29000 gas left after the call is enough to update s_tail and the caller's balance. |
||||
// Hence tokens will have been destroyed without a commensurate gas refund. |
||||
// Fortunately, there is a simple workaround: |
||||
// Whenever you call free, freeUpTo, freeFrom, or freeUpToFrom, ensure that you pass at least |
||||
// 25710 + `value` * (1148 + 5722 + 150) gas. (It won't all be used) |
||||
function destroyChildren(uint256 value) internal { |
||||
uint256 tail = s_tail; |
||||
// tail points to slot behind the last contract in the queue |
||||
for (uint256 i = tail + 1; i <= tail + value; i++) { |
||||
mk_contract_address(this, i).call(); |
||||
} |
||||
|
||||
s_tail = tail + value; |
||||
} |
||||
|
||||
// Frees `value` sub-tokens (e.g. cents, pennies, ...) belonging to the |
||||
// caller of this function by destroying `value` child contracts, which |
||||
// will trigger a partial gas refund. |
||||
// You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas |
||||
// when calling this function. For details, see the comment above `destroyChilden`. |
||||
function free(uint256 value) public returns (bool success) { |
||||
uint256 from_balance = s_balances[msg.sender]; |
||||
if (value > from_balance) { |
||||
return false; |
||||
} |
||||
|
||||
destroyChildren(value); |
||||
|
||||
s_balances[msg.sender] = from_balance - value; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
// Frees up to `value` sub-tokens. Returns how many tokens were freed. |
||||
// Otherwise, identical to free. |
||||
// You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas |
||||
// when calling this function. For details, see the comment above `destroyChilden`. |
||||
function freeUpTo(uint256 value) public returns (uint256 freed) { |
||||
uint256 from_balance = s_balances[msg.sender]; |
||||
if (value > from_balance) { |
||||
value = from_balance; |
||||
} |
||||
|
||||
destroyChildren(value); |
||||
|
||||
s_balances[msg.sender] = from_balance - value; |
||||
|
||||
return value; |
||||
} |
||||
|
||||
// Frees `value` sub-tokens owned by address `from`. Requires that `msg.sender` |
||||
// has been approved by `from`. |
||||
// You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas |
||||
// when calling this function. For details, see the comment above `destroyChilden`. |
||||
function freeFrom(address from, uint256 value) public returns (bool success) { |
||||
address spender = msg.sender; |
||||
uint256 from_balance = s_balances[from]; |
||||
if (value > from_balance) { |
||||
return false; |
||||
} |
||||
|
||||
mapping(address => uint256) from_allowances = s_allowances[from]; |
||||
uint256 spender_allowance = from_allowances[spender]; |
||||
if (value > spender_allowance) { |
||||
return false; |
||||
} |
||||
|
||||
destroyChildren(value); |
||||
|
||||
s_balances[from] = from_balance - value; |
||||
from_allowances[spender] = spender_allowance - value; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
// Frees up to `value` sub-tokens owned by address `from`. Returns how many tokens were freed. |
||||
// Otherwise, identical to `freeFrom`. |
||||
// You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas |
||||
// when calling this function. For details, see the comment above `destroyChilden`. |
||||
function freeFromUpTo(address from, uint256 value) public returns (uint256 freed) { |
||||
address spender = msg.sender; |
||||
uint256 from_balance = s_balances[from]; |
||||
if (value > from_balance) { |
||||
value = from_balance; |
||||
} |
||||
|
||||
mapping(address => uint256) from_allowances = s_allowances[from]; |
||||
uint256 spender_allowance = from_allowances[spender]; |
||||
if (value > spender_allowance) { |
||||
value = spender_allowance; |
||||
} |
||||
|
||||
destroyChildren(value); |
||||
|
||||
s_balances[from] = from_balance - value; |
||||
from_allowances[spender] = spender_allowance - value; |
||||
|
||||
return value; |
||||
} |
||||
} |
@ -0,0 +1,880 @@ |
||||
pragma solidity ^0.4.11; |
||||
|
||||
/* |
||||
Owned contract interface |
||||
*/ |
||||
contract IOwned { |
||||
// this function isn't abstract since the compiler emits automatically generated getter functions as external |
||||
function owner() public constant returns (address owner) { owner; } |
||||
|
||||
function transferOwnership(address _newOwner) public; |
||||
function acceptOwnership() public; |
||||
} |
||||
|
||||
/* |
||||
ERC20 Standard Token interface |
||||
*/ |
||||
contract IERC20Token { |
||||
// these functions aren't abstract since the compiler emits automatically generated getter functions as external |
||||
function name() public constant returns (string name) { name; } |
||||
function symbol() public constant returns (string symbol) { symbol; } |
||||
function decimals() public constant returns (uint8 decimals) { decimals; } |
||||
function totalSupply() public constant returns (uint256 totalSupply) { totalSupply; } |
||||
function balanceOf(address _owner) public constant returns (uint256 balance) { _owner; balance; } |
||||
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) { _owner; _spender; remaining; } |
||||
|
||||
function transfer(address _to, uint256 _value) public returns (bool success); |
||||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); |
||||
function approve(address _spender, uint256 _value) public returns (bool success); |
||||
} |
||||
|
||||
/* |
||||
Token Holder interface |
||||
*/ |
||||
contract ITokenHolder is IOwned { |
||||
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public; |
||||
} |
||||
|
||||
/* |
||||
Smart Token interface |
||||
*/ |
||||
contract ISmartToken is ITokenHolder, IERC20Token { |
||||
function disableTransfers(bool _disable) public; |
||||
function issue(address _to, uint256 _amount) public; |
||||
function destroy(address _from, uint256 _amount) public; |
||||
} |
||||
|
||||
/* |
||||
Overflow protected math functions |
||||
*/ |
||||
contract SafeMath { |
||||
/** |
||||
constructor |
||||
*/ |
||||
function SafeMath() { |
||||
} |
||||
|
||||
/** |
||||
@dev returns the sum of _x and _y, asserts if the calculation overflows |
||||
|
||||
@param _x value 1 |
||||
@param _y value 2 |
||||
|
||||
@return sum |
||||
*/ |
||||
function safeAdd(uint256 _x, uint256 _y) internal returns (uint256) { |
||||
uint256 z = _x + _y; |
||||
assert(z >= _x); |
||||
return z; |
||||
} |
||||
|
||||
/** |
||||
@dev returns the difference of _x minus _y, asserts if the subtraction results in a negative number |
||||
|
||||
@param _x minuend |
||||
@param _y subtrahend |
||||
|
||||
@return difference |
||||
*/ |
||||
function safeSub(uint256 _x, uint256 _y) internal returns (uint256) { |
||||
assert(_x >= _y); |
||||
return _x - _y; |
||||
} |
||||
|
||||
/** |
||||
@dev returns the product of multiplying _x by _y, asserts if the calculation overflows |
||||
|
||||
@param _x factor 1 |
||||
@param _y factor 2 |
||||
|
||||
@return product |
||||
*/ |
||||
function safeMul(uint256 _x, uint256 _y) internal returns (uint256) { |
||||
uint256 z = _x * _y; |
||||
assert(_x == 0 || z / _x == _y); |
||||
return z; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
ERC20 Standard Token implementation |
||||
*/ |
||||
contract ERC20Token is IERC20Token, SafeMath { |
||||
string public standard = 'Token 0.1'; |
||||
string public name = ''; |
||||
string public symbol = ''; |
||||
uint8 public decimals = 0; |
||||
uint256 public totalSupply = 0; |
||||
mapping (address => uint256) public balanceOf; |
||||
mapping (address => mapping (address => uint256)) public allowance; |
||||
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _value); |
||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value); |
||||
|
||||
/** |
||||
@dev constructor |
||||
|
||||
@param _name token name |
||||
@param _symbol token symbol |
||||
@param _decimals decimal points, for display purposes |
||||
*/ |
||||
function ERC20Token(string _name, string _symbol, uint8 _decimals) { |
||||
require(bytes(_name).length > 0 && bytes(_symbol).length > 0); // validate input |
||||
|
||||
name = _name; |
||||
symbol = _symbol; |
||||
decimals = _decimals; |
||||
} |
||||
|
||||
// validates an address - currently only checks that it isn't null |
||||
modifier validAddress(address _address) { |
||||
require(_address != 0x0); |
||||
_; |
||||
} |
||||
|
||||
/** |
||||
@dev send coins |
||||
throws on any error rather then return a false flag to minimize user errors |
||||
|
||||
@param _to target address |
||||
@param _value transfer amount |
||||
|
||||
@return true if the transfer was successful, false if it wasn't |
||||
*/ |
||||
function transfer(address _to, uint256 _value) |
||||
public |
||||
validAddress(_to) |
||||
returns (bool success) |
||||
{ |
||||
balanceOf[msg.sender] = safeSub(balanceOf[msg.sender], _value); |
||||
balanceOf[_to] = safeAdd(balanceOf[_to], _value); |
||||
Transfer(msg.sender, _to, _value); |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
@dev an account/contract attempts to get the coins |
||||
throws on any error rather then return a false flag to minimize user errors |
||||
|
||||
@param _from source address |
||||
@param _to target address |
||||
@param _value transfer amount |
||||
|
||||
@return true if the transfer was successful, false if it wasn't |
||||
*/ |
||||
function transferFrom(address _from, address _to, uint256 _value) |
||||
public |
||||
validAddress(_from) |
||||
validAddress(_to) |
||||
returns (bool success) |
||||
{ |
||||
allowance[_from][msg.sender] = safeSub(allowance[_from][msg.sender], _value); |
||||
balanceOf[_from] = safeSub(balanceOf[_from], _value); |
||||
balanceOf[_to] = safeAdd(balanceOf[_to], _value); |
||||
Transfer(_from, _to, _value); |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
@dev allow another account/contract to spend some tokens on your behalf |
||||
throws on any error rather then return a false flag to minimize user errors |
||||
|
||||
also, to minimize the risk of the approve/transferFrom attack vector |
||||
(see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice |
||||
in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value |
||||
|
||||
@param _spender approved address |
||||
@param _value allowance amount |
||||
|
||||
@return true if the approval was successful, false if it wasn't |
||||
*/ |
||||
function approve(address _spender, uint256 _value) |
||||
public |
||||
validAddress(_spender) |
||||
returns (bool success) |
||||
{ |
||||
// if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal |
||||
require(_value == 0 || allowance[msg.sender][_spender] == 0); |
||||
|
||||
allowance[msg.sender][_spender] = _value; |
||||
Approval(msg.sender, _spender, _value); |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
/* |
||||
Provides support and utilities for contract ownership |
||||
*/ |
||||
contract Owned is IOwned { |
||||
address public owner; |
||||
address public newOwner; |
||||
|
||||
event OwnerUpdate(address _prevOwner, address _newOwner); |
||||
|
||||
/** |
||||
@dev constructor |
||||
*/ |
||||
function Owned() { |
||||
owner = msg.sender; |
||||
} |
||||
|
||||
// allows execution by the owner only |
||||
modifier ownerOnly { |
||||
assert(msg.sender == owner); |
||||
_; |
||||
} |
||||
|
||||
/** |
||||
@dev allows transferring the contract ownership |
||||
the new owner still need to accept the transfer |
||||
can only be called by the contract owner |
||||
|
||||
@param _newOwner new contract owner |
||||
*/ |
||||
function transferOwnership(address _newOwner) public ownerOnly { |
||||
require(_newOwner != owner); |
||||
newOwner = _newOwner; |
||||
} |
||||
|
||||
/** |
||||
@dev used by a new owner to accept an ownership transfer |
||||
*/ |
||||
function acceptOwnership() public { |
||||
require(msg.sender == newOwner); |
||||
OwnerUpdate(owner, newOwner); |
||||
owner = newOwner; |
||||
newOwner = 0x0; |
||||
} |
||||
} |
||||
|
||||
/* |
||||
We consider every contract to be a 'token holder' since it's currently not possible |
||||
for a contract to deny receiving tokens. |
||||
|
||||
The TokenHolder's contract sole purpose is to provide a safety mechanism that allows |
||||
the owner to send tokens that were sent to the contract by mistake back to their sender. |
||||
*/ |
||||
contract TokenHolder is ITokenHolder, Owned { |
||||
/** |
||||
@dev constructor |
||||
*/ |
||||
function TokenHolder() { |
||||
} |
||||
|
||||
// validates an address - currently only checks that it isn't null |
||||
modifier validAddress(address _address) { |
||||
require(_address != 0x0); |
||||
_; |
||||
} |
||||
|
||||
// verifies that the address is different than this contract address |
||||
modifier notThis(address _address) { |
||||
require(_address != address(this)); |
||||
_; |
||||
} |
||||
|
||||
/** |
||||
@dev withdraws tokens held by the contract and sends them to an account |
||||
can only be called by the owner |
||||
|
||||
@param _token ERC20 token contract address |
||||
@param _to account to receive the new amount |
||||
@param _amount amount to withdraw |
||||
*/ |
||||
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) |
||||
public |
||||
ownerOnly |
||||
validAddress(_token) |
||||
validAddress(_to) |
||||
notThis(_to) |
||||
{ |
||||
assert(_token.transfer(_to, _amount)); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
Smart Token v0.2 |
||||
|
||||
'Owned' is specified here for readability reasons |
||||
*/ |
||||
contract SmartToken is ISmartToken, ERC20Token, Owned, TokenHolder { |
||||
string public version = '0.2'; |
||||
|
||||
bool public transfersEnabled = true; // true if transfer/transferFrom are enabled, false if not |
||||
|
||||
// triggered when a smart token is deployed - the _token address is defined for forward compatibility, in case we want to trigger the event from a factory |
||||
event NewSmartToken(address _token); |
||||
// triggered when the total supply is increased |
||||
event Issuance(uint256 _amount); |
||||
// triggered when the total supply is decreased |
||||
event Destruction(uint256 _amount); |
||||
|
||||
/** |
||||
@dev constructor |
||||
|
||||
@param _name token name |
||||
@param _symbol token short symbol, 1-6 characters |
||||
@param _decimals for display purposes only |
||||
*/ |
||||
function SmartToken(string _name, string _symbol, uint8 _decimals) |
||||
ERC20Token(_name, _symbol, _decimals) |
||||
{ |
||||
require(bytes(_symbol).length <= 6); // validate input |
||||
NewSmartToken(address(this)); |
||||
} |
||||
|
||||
// allows execution only when transfers aren't disabled |
||||
modifier transfersAllowed { |
||||
assert(transfersEnabled); |
||||
_; |
||||
} |
||||
|
||||
/** |
||||
@dev disables/enables transfers |
||||
can only be called by the contract owner |
||||
|
||||
@param _disable true to disable transfers, false to enable them |
||||
*/ |
||||
function disableTransfers(bool _disable) public ownerOnly { |
||||
transfersEnabled = !_disable; |
||||
} |
||||
|
||||
/** |
||||
@dev increases the token supply and sends the new tokens to an account |
||||
can only be called by the contract owner |
||||
|
||||
@param _to account to receive the new amount |
||||
@param _amount amount to increase the supply by |
||||
*/ |
||||
function issue(address _to, uint256 _amount) |
||||
public |
||||
ownerOnly |
||||
validAddress(_to) |
||||
notThis(_to) |
||||
{ |
||||
totalSupply = safeAdd(totalSupply, _amount); |
||||
balanceOf[_to] = safeAdd(balanceOf[_to], _amount); |
||||
|
||||
Issuance(_amount); |
||||
Transfer(this, _to, _amount); |
||||
} |
||||
|
||||
/** |
||||
@dev removes tokens from an account and decreases the token supply |
||||
can only be called by the contract owner |
||||
|
||||
@param _from account to remove the amount from |
||||
@param _amount amount to decrease the supply by |
||||
*/ |
||||
function destroy(address _from, uint256 _amount) |
||||
public |
||||
ownerOnly |
||||
{ |
||||
balanceOf[_from] = safeSub(balanceOf[_from], _amount); |
||||
totalSupply = safeSub(totalSupply, _amount); |
||||
|
||||
Transfer(_from, this, _amount); |
||||
Destruction(_amount); |
||||
} |
||||
|
||||
// ERC20 standard method overrides with some extra functionality |
||||
|
||||
/** |
||||
@dev send coins |
||||
throws on any error rather then return a false flag to minimize user errors |
||||
note that when transferring to the smart token's address, the coins are actually destroyed |
||||
|
||||
@param _to target address |
||||
@param _value transfer amount |
||||
|
||||
@return true if the transfer was successful, false if it wasn't |
||||
*/ |
||||
function transfer(address _to, uint256 _value) public transfersAllowed returns (bool success) { |
||||
assert(super.transfer(_to, _value)); |
||||
|
||||
// transferring to the contract address destroys tokens |
||||
if (_to == address(this)) { |
||||
balanceOf[_to] -= _value; |
||||
totalSupply -= _value; |
||||
Destruction(_value); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
@dev an account/contract attempts to get the coins |
||||
throws on any error rather then return a false flag to minimize user errors |
||||
note that when transferring to the smart token's address, the coins are actually destroyed |
||||
|
||||
@param _from source address |
||||
@param _to target address |
||||
@param _value transfer amount |
||||
|
||||
@return true if the transfer was successful, false if it wasn't |
||||
*/ |
||||
function transferFrom(address _from, address _to, uint256 _value) public transfersAllowed returns (bool success) { |
||||
assert(super.transferFrom(_from, _to, _value)); |
||||
|
||||
// transferring to the contract address destroys tokens |
||||
if (_to == address(this)) { |
||||
balanceOf[_to] -= _value; |
||||
totalSupply -= _value; |
||||
Destruction(_value); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
|
||||
/// @title Ownable |
||||
/// @dev The Ownable contract has an owner address, and provides basic authorization control functions, this simplifies |
||||
/// & the implementation of "user permissions". |
||||
contract Ownable { |
||||
address public owner; |
||||
address public newOwnerCandidate; |
||||
|
||||
event OwnershipRequested(address indexed _by, address indexed _to); |
||||
event OwnershipTransferred(address indexed _from, address indexed _to); |
||||
|
||||
/// @dev The Ownable constructor sets the original `owner` of the contract to the sender account. |
||||
function Ownable() { |
||||
owner = msg.sender; |
||||
} |
||||
|
||||
/// @dev Throws if called by any account other than the owner. |
||||
modifier onlyOwner() { |
||||
if (msg.sender != owner) { |
||||
throw; |
||||
} |
||||
|
||||
_; |
||||
} |
||||
|
||||
/// @dev Proposes to transfer control of the contract to a newOwnerCandidate. |
||||
/// @param _newOwnerCandidate address The address to transfer ownership to. |
||||
function transferOwnership(address _newOwnerCandidate) onlyOwner { |
||||
require(_newOwnerCandidate != address(0)); |
||||
|
||||
newOwnerCandidate = _newOwnerCandidate; |
||||
|
||||
OwnershipRequested(msg.sender, newOwnerCandidate); |
||||
} |
||||
|
||||
/// @dev Accept ownership transfer. This method needs to be called by the perviously proposed owner. |
||||
function acceptOwnership() { |
||||
if (msg.sender == newOwnerCandidate) { |
||||
owner = newOwnerCandidate; |
||||
newOwnerCandidate = address(0); |
||||
|
||||
OwnershipTransferred(owner, newOwnerCandidate); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// @title Math operations with safety checks |
||||
library SaferMath { |
||||
function mul(uint256 a, uint256 b) internal returns (uint256) { |
||||
uint256 c = a * b; |
||||
assert(a == 0 || c / a == b); |
||||
return c; |
||||
} |
||||
|
||||
function div(uint256 a, uint256 b) internal returns (uint256) { |
||||
// assert(b > 0); // Solidity automatically throws when dividing by 0 |
||||
uint256 c = a / b; |
||||
// assert(a == b * c + a % b); // There is no case in which this doesn't hold |
||||
return c; |
||||
} |
||||
|
||||
function sub(uint256 a, uint256 b) internal returns (uint256) { |
||||
assert(b <= a); |
||||
return a - b; |
||||
} |
||||
|
||||
function add(uint256 a, uint256 b) internal returns (uint256) { |
||||
uint256 c = a + b; |
||||
assert(c >= a); |
||||
return c; |
||||
} |
||||
|
||||
function max64(uint64 a, uint64 b) internal constant returns (uint64) { |
||||
return a >= b ? a : b; |
||||
} |
||||
|
||||
function min64(uint64 a, uint64 b) internal constant returns (uint64) { |
||||
return a < b ? a : b; |
||||
} |
||||
|
||||
function max256(uint256 a, uint256 b) internal constant returns (uint256) { |
||||
return a >= b ? a : b; |
||||
} |
||||
|
||||
function min256(uint256 a, uint256 b) internal constant returns (uint256) { |
||||
return a < b ? a : b; |
||||
} |
||||
} |
||||
|
||||
|
||||
/// @title Stox Smart Token |
||||
contract StoxSmartToken is SmartToken { |
||||
function StoxSmartToken() SmartToken('Stox', 'STX', 18) { |
||||
disableTransfers(true); |
||||
} |
||||
} |
||||
|
||||
|
||||
/// @title Vesting trustee |
||||
contract Trustee is Ownable { |
||||
using SaferMath for uint256; |
||||
|
||||
// The address of the STX ERC20 token. |
||||
StoxSmartToken public stox; |
||||
|
||||
struct Grant { |
||||
uint256 value; |
||||
uint256 start; |
||||
uint256 cliff; |
||||
uint256 end; |
||||
uint256 transferred; |
||||
bool revokable; |
||||
} |
||||
|
||||
// Grants holder. |
||||
mapping (address => Grant) public grants; |
||||
|
||||
// Total tokens available for vesting. |
||||
uint256 public totalVesting; |
||||
|
||||
event NewGrant(address indexed _from, address indexed _to, uint256 _value); |
||||
event UnlockGrant(address indexed _holder, uint256 _value); |
||||
event RevokeGrant(address indexed _holder, uint256 _refund); |
||||
|
||||
/// @dev Constructor that initializes the address of the StoxSmartToken contract. |
||||
/// @param _stox StoxSmartToken The address of the previously deployed StoxSmartToken smart contract. |
||||
function Trustee(StoxSmartToken _stox) { |
||||
require(_stox != address(0)); |
||||
|
||||
stox = _stox; |
||||
} |
||||
|
||||
/// @dev Grant tokens to a specified address. |
||||
/// @param _to address The address to grant tokens to. |
||||
/// @param _value uint256 The amount of tokens to be granted. |
||||
/// @param _start uint256 The beginning of the vesting period. |
||||
/// @param _cliff uint256 Duration of the cliff period. |
||||
/// @param _end uint256 The end of the vesting period. |
||||
/// @param _revokable bool Whether the grant is revokable or not. |
||||
function grant(address _to, uint256 _value, uint256 _start, uint256 _cliff, uint256 _end, bool _revokable) |
||||
public onlyOwner { |
||||
require(_to != address(0)); |
||||
require(_value > 0); |
||||
|
||||
// Make sure that a single address can be granted tokens only once. |
||||
require(grants[_to].value == 0); |
||||
|
||||
// Check for date inconsistencies that may cause unexpected behavior. |
||||
require(_start <= _cliff && _cliff <= _end); |
||||
|
||||
// Check that this grant doesn't exceed the total amount of tokens currently available for vesting. |
||||
require(totalVesting.add(_value) <= stox.balanceOf(address(this))); |
||||
|
||||
// Assign a new grant. |
||||
grants[_to] = Grant({ |
||||
value: _value, |
||||
start: _start, |
||||
cliff: _cliff, |
||||
end: _end, |
||||
transferred: 0, |
||||
revokable: _revokable |
||||
}); |
||||
|
||||
// Tokens granted, reduce the total amount available for vesting. |
||||
totalVesting = totalVesting.add(_value); |
||||
|
||||
NewGrant(msg.sender, _to, _value); |
||||
} |
||||
|
||||
/// @dev Revoke the grant of tokens of a specifed address. |
||||
/// @param _holder The address which will have its tokens revoked. |
||||
function revoke(address _holder) public onlyOwner { |
||||
Grant grant = grants[_holder]; |
||||
|
||||
require(grant.revokable); |
||||
|
||||
// Send the remaining STX back to the owner. |
||||
uint256 refund = grant.value.sub(grant.transferred); |
||||
|
||||
// Remove the grant. |
||||
delete grants[_holder]; |
||||
|
||||
totalVesting = totalVesting.sub(refund); |
||||
stox.transfer(msg.sender, refund); |
||||
|
||||
RevokeGrant(_holder, refund); |
||||
} |
||||
|
||||
/// @dev Calculate the total amount of vested tokens of a holder at a given time. |
||||
/// @param _holder address The address of the holder. |
||||
/// @param _time uint256 The specific time. |
||||
/// @return a uint256 representing a holder's total amount of vested tokens. |
||||
function vestedTokens(address _holder, uint256 _time) public constant returns (uint256) { |
||||
Grant grant = grants[_holder]; |
||||
if (grant.value == 0) { |
||||
return 0; |
||||
} |
||||
|
||||
return calculateVestedTokens(grant, _time); |
||||
} |
||||
|
||||
/// @dev Calculate amount of vested tokens at a specifc time. |
||||
/// @param _grant Grant The vesting grant. |
||||
/// @param _time uint256 The time to be checked |
||||
/// @return An uint256 representing the amount of vested tokens of a specific grant. |
||||
/// | _/-------- vestedTokens rect |
||||
/// | _/ |
||||
/// | _/ |
||||
/// | _/ |
||||
/// | _/ |
||||
/// | / |
||||
/// | .| |
||||
/// | . | |
||||
/// | . | |
||||
/// | . | |
||||
/// | . | |
||||
/// | . | |
||||
/// +===+===========+---------+----------> time |
||||
/// Start Cliff End |
||||
function calculateVestedTokens(Grant _grant, uint256 _time) private constant returns (uint256) { |
||||
// If we're before the cliff, then nothing is vested. |
||||
if (_time < _grant.cliff) { |
||||
return 0; |
||||
} |
||||
|
||||
// If we're after the end of the vesting period - everything is vested; |
||||
if (_time >= _grant.end) { |
||||
return _grant.value; |
||||
} |
||||
|
||||
// Interpolate all vested tokens: vestedTokens = tokens/// (time - start) / (end - start) |
||||
return _grant.value.mul(_time.sub(_grant.start)).div(_grant.end.sub(_grant.start)); |
||||
} |
||||
|
||||
/// @dev Unlock vested tokens and transfer them to their holder. |
||||
/// @return a uint256 representing the amount of vested tokens transferred to their holder. |
||||
function unlockVestedTokens() public { |
||||
Grant grant = grants[msg.sender]; |
||||
require(grant.value != 0); |
||||
|
||||
// Get the total amount of vested tokens, acccording to grant. |
||||
uint256 vested = calculateVestedTokens(grant, now); |
||||
if (vested == 0) { |
||||
return; |
||||
} |
||||
|
||||
// Make sure the holder doesn't transfer more than what he already has. |
||||
uint256 transferable = vested.sub(grant.transferred); |
||||
if (transferable == 0) { |
||||
return; |
||||
} |
||||
|
||||
grant.transferred = grant.transferred.add(transferable); |
||||
totalVesting = totalVesting.sub(transferable); |
||||
stox.transfer(msg.sender, transferable); |
||||
|
||||
UnlockGrant(msg.sender, transferable); |
||||
} |
||||
} |
||||
|
||||
|
||||
/// @title Stox Smart Token sale |
||||
contract StoxSmartTokenSale is Ownable { |
||||
using SaferMath for uint256; |
||||
|
||||
uint256 public constant DURATION = 14 days; |
||||
|
||||
bool public isFinalized = false; |
||||
bool public isDistributed = false; |
||||
|
||||
// The address of the STX ERC20 token. |
||||
StoxSmartToken public stox; |
||||
|
||||
// The address of the token allocation trustee; |
||||
Trustee public trustee; |
||||
|
||||
uint256 public startTime = 0; |
||||
uint256 public endTime = 0; |
||||
address public fundingRecipient; |
||||
|
||||
uint256 public tokensSold = 0; |
||||
|
||||
// TODO: update to the correct values. |
||||
uint256 public constant ETH_CAP = 148000; |
||||
uint256 public constant EXCHANGE_RATE = 200; // 200 STX for ETH |
||||
uint256 public constant TOKEN_SALE_CAP = ETH_CAP * EXCHANGE_RATE * 10 ** 18; |
||||
|
||||
event TokensIssued(address indexed _to, uint256 _tokens); |
||||
|
||||
/// @dev Throws if called when not during sale. |
||||
modifier onlyDuringSale() { |
||||
if (tokensSold >= TOKEN_SALE_CAP || now < startTime || now >= endTime) { |
||||
throw; |
||||
} |
||||
|
||||
_; |
||||
} |
||||
|
||||
/// @dev Throws if called before sale ends. |
||||
modifier onlyAfterSale() { |
||||
if (!(tokensSold >= TOKEN_SALE_CAP || now >= endTime)) { |
||||
throw; |
||||
} |
||||
|
||||
_; |
||||
} |
||||
|
||||
/// @dev Constructor that initializes the sale conditions. |
||||
/// @param _fundingRecipient address The address of the funding recipient. |
||||
/// @param _startTime uint256 The start time of the token sale. |
||||
function StoxSmartTokenSale(address _stox, address _fundingRecipient, uint256 _startTime) { |
||||
require(_stox != address(0)); |
||||
require(_fundingRecipient != address(0)); |
||||
require(_startTime > now); |
||||
|
||||
stox = StoxSmartToken(_stox); |
||||
|
||||
fundingRecipient = _fundingRecipient; |
||||
startTime = _startTime; |
||||
endTime = startTime + DURATION; |
||||
} |
||||
|
||||
/// @dev Distributed tokens to the partners who have participated during the pre-sale. |
||||
function distributePartnerTokens() external onlyOwner { |
||||
require(!isDistributed); |
||||
|
||||
assert(tokensSold == 0); |
||||
assert(stox.totalSupply() == 0); |
||||
|
||||
// Distribute strategic tokens to partners. Please note, that this address doesn't represent a single entity or |
||||
// person and will be only used to distribute tokens to 30~ partners. |
||||
// |
||||
// Please expect to see token transfers from this address in the first 24 hours after the token sale ends. |
||||
issueTokens(0x9065260ef6830f6372F1Bde408DeC57Fe3150530, 14800000 * 10 ** 18); |
||||
|
||||
isDistributed = true; |
||||
} |
||||
|
||||
/// @dev Finalizes the token sale event. |
||||
function finalize() external onlyAfterSale { |
||||
if (isFinalized) { |
||||
throw; |
||||
} |
||||
|
||||
// Grant vesting grants. |
||||
// |
||||
// TODO: use real addresses. |
||||
trustee = new Trustee(stox); |
||||
|
||||
// Since only 50% of the tokens will be sold, we will automatically issue the same amount of sold STX to the |
||||
// trustee. |
||||
uint256 unsoldTokens = tokensSold; |
||||
|
||||
// Issue 55% of the remaining tokens (== 27.5%) go to strategic parternships. |
||||
uint256 strategicPartnershipTokens = unsoldTokens.mul(55).div(100); |
||||
|
||||
// Note: we will substract the bonus tokens from this grant, since they were already issued for the pre-sale |
||||
// strategic partners and should've been taken from this allocation. |
||||
stox.issue(0xbC14105ccDdeAadB96Ba8dCE18b40C45b6bACf58, strategicPartnershipTokens); |
||||
|
||||
// Issue the remaining tokens as vesting grants: |
||||
stox.issue(trustee, unsoldTokens.sub(strategicPartnershipTokens)); |
||||
|
||||
// 25% of the remaining tokens (== 12.5%) go to Invest.com, at uniform 12 months vesting schedule. |
||||
trustee.grant(0xb54c6a870d4aD65e23d471Fb7941aD271D323f5E, unsoldTokens.mul(25).div(100), now, now, |
||||
now.add(1 years), true); |
||||
|
||||
// 20% of the remaining tokens (== 10%) go to Stox team, at uniform 24 months vesting schedule. |
||||
trustee.grant(0x4eB4Cd1D125d9d281709Ff38d65b99a6927b46c1, unsoldTokens.mul(20).div(100), now, now, |
||||
now.add(2 years), true); |
||||
|
||||
// Re-enable transfers after the token sale. |
||||
stox.disableTransfers(false); |
||||
|
||||
isFinalized = true; |
||||
} |
||||
|
||||
/// @dev Create and sell tokens to the caller. |
||||
/// @param _recipient address The address of the recipient. |
||||
function create(address _recipient) public payable onlyDuringSale { |
||||
require(_recipient != address(0)); |
||||
require(msg.value > 0); |
||||
|
||||
assert(isDistributed); |
||||
|
||||
uint256 tokens = SaferMath.min256(msg.value.mul(EXCHANGE_RATE), TOKEN_SALE_CAP.sub(tokensSold)); |
||||
uint256 contribution = tokens.div(EXCHANGE_RATE); |
||||
|
||||
issueTokens(_recipient, tokens); |
||||
|
||||
// Transfer the funds to the funding recipient. |
||||
fundingRecipient.transfer(contribution); |
||||
|
||||
// Refund the msg.sender, in the case that not all of its ETH was used. This can happen only when selling the |
||||
// last chunk of STX. |
||||
uint256 refund = msg.value.sub(contribution); |
||||
if (refund > 0) { |
||||
msg.sender.transfer(refund); |
||||
} |
||||
} |
||||
|
||||
/// @dev Issues tokens for the recipient. |
||||
/// @param _recipient address The address of the recipient. |
||||
/// @param _tokens uint256 The amount of tokens to issue. |
||||
function issueTokens(address _recipient, uint256 _tokens) private { |
||||
// Update total sold tokens. |
||||
tokensSold = tokensSold.add(_tokens); |
||||
|
||||
stox.issue(_recipient, _tokens); |
||||
|
||||
TokensIssued(_recipient, _tokens); |
||||
} |
||||
|
||||
/// @dev Fallback function that will delegate the request to create. |
||||
function () external payable onlyDuringSale { |
||||
create(msg.sender); |
||||
} |
||||
|
||||
/// @dev Proposes to transfer control of the StoxSmartToken contract to a new owner. |
||||
/// @param _newOwnerCandidate address The address to transfer ownership to. |
||||
/// |
||||
/// Note that: |
||||
/// 1. The new owner will need to call StoxSmartToken's acceptOwnership directly in order to accept the ownership. |
||||
/// 2. Calling this method during the token sale will prevent the token sale to continue, since only the owner of |
||||
/// the StoxSmartToken contract can issue new tokens. |
||||
function transferSmartTokenOwnership(address _newOwnerCandidate) external onlyOwner { |
||||
stox.transferOwnership(_newOwnerCandidate); |
||||
} |
||||
|
||||
/// @dev Accepts new ownership on behalf of the StoxSmartToken contract. This can be used, by the token sale |
||||
/// contract itself to claim back ownership of the StoxSmartToken contract. |
||||
function acceptSmartTokenOwnership() external onlyOwner { |
||||
stox.acceptOwnership(); |
||||
} |
||||
|
||||
/// @dev Proposes to transfer control of the Trustee contract to a new owner. |
||||
/// @param _newOwnerCandidate address The address to transfer ownership to. |
||||
/// |
||||
/// Note that: |
||||
/// 1. The new owner will need to call Trustee's acceptOwnership directly in order to accept the ownership. |
||||
/// 2. Calling this method during the token sale won't be possible, as the Trustee is only created after its |
||||
/// finalization. |
||||
function transferTrusteeOwnership(address _newOwnerCandidate) external onlyOwner { |
||||
trustee.transferOwnership(_newOwnerCandidate); |
||||
} |
||||
|
||||
/// @dev Accepts new ownership on behalf of the Trustee contract. This can be used, by the token sale |
||||
/// contract itself to claim back ownership of the Trustee contract. |
||||
function acceptTrusteeOwnership() external onlyOwner { |
||||
trustee.acceptOwnership(); |
||||
} |
||||
} |
@ -0,0 +1,241 @@ |
||||
pragma solidity ^0.4.17; |
||||
|
||||
contract Cofounded { |
||||
mapping (address => uint) public cofounderIndices; |
||||
address[] public cofounders; |
||||
|
||||
|
||||
/// @dev restrict execution to one of original cofounder addresses |
||||
modifier restricted () { |
||||
uint cofounderIndex = cofounderIndices[msg.sender]; |
||||
require(msg.sender == cofounders[cofounderIndex]); |
||||
_; |
||||
} |
||||
|
||||
/// @notice creates the Cofounded contract instance |
||||
/// @dev adds up to cofounders. |
||||
/// also adds the deployment address as a cofounder |
||||
function Cofounded (address[] contractCofounders) public { |
||||
cofounders.push(msg.sender); |
||||
|
||||
for (uint8 x = 0; x < contractCofounders.length; x++) { |
||||
address cofounder = contractCofounders[x]; |
||||
|
||||
bool isValidUniqueCofounder = |
||||
cofounder != address(0) && |
||||
cofounder != msg.sender && |
||||
cofounderIndices[cofounder] == 0; |
||||
|
||||
|
||||
// NOTE: solidity as of 0.4.20 does not have an |
||||
// undefined or null-like value |
||||
// thusly mappings return the default value of the value type |
||||
// for an unregistered key value |
||||
// an address which doesn't exist will return 0 |
||||
// which is actually the index of the address of the first |
||||
// cofounder |
||||
if (isValidUniqueCofounder) { |
||||
uint256 cofounderIndex = cofounders.push(cofounder) - 1; |
||||
cofounderIndices[cofounder] = cofounderIndex; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// @dev get count of cofounders |
||||
function getCofounderCount () public constant returns (uint256) { |
||||
return cofounders.length; |
||||
} |
||||
|
||||
/// @dev get list of cofounders |
||||
function getCofounders () public constant returns (address[]) { |
||||
return cofounders; |
||||
} |
||||
} |
||||
|
||||
interface ERC20 { |
||||
|
||||
// Required methods |
||||
function transfer (address to, uint256 value) public returns (bool success); |
||||
function transferFrom (address from, address to, uint256 value) public returns (bool success); |
||||
function approve (address spender, uint256 value) public returns (bool success); |
||||
function allowance (address owner, address spender) public constant returns (uint256 remaining); |
||||
function balanceOf (address owner) public constant returns (uint256 balance); |
||||
// Events |
||||
event Transfer (address indexed from, address indexed to, uint256 value); |
||||
event Approval (address indexed owner, address indexed spender, uint256 value); |
||||
} |
||||
|
||||
|
||||
/// @title Interface for contracts conforming to ERC-165: Pseudo-Introspection, or standard interface detection |
||||
/// @author Mish Ochu |
||||
interface ERC165 { |
||||
/// @dev true iff the interface is supported |
||||
function supportsInterface(bytes4 interfaceID) external constant returns (bool); |
||||
} |
||||
contract InterfaceSignatureConstants { |
||||
bytes4 constant InterfaceSignature_ERC165 = |
||||
bytes4(keccak256('supportsInterface(bytes4)')); |
||||
|
||||
bytes4 constant InterfaceSignature_ERC20 = |
||||
bytes4(keccak256('totalSupply()')) ^ |
||||
bytes4(keccak256('balanceOf(address)')) ^ |
||||
bytes4(keccak256('transfer(address,uint256)')) ^ |
||||
bytes4(keccak256('transferFrom(address,address,uint256)')) ^ |
||||
bytes4(keccak256('approve(address,uint256)')) ^ |
||||
bytes4(keccak256('allowance(address,address)')); |
||||
|
||||
bytes4 constant InterfaceSignature_ERC20_PlusOptions = |
||||
bytes4(keccak256('name()')) ^ |
||||
bytes4(keccak256('symbol()')) ^ |
||||
bytes4(keccak256('decimals()')) ^ |
||||
bytes4(keccak256('totalSupply()')) ^ |
||||
bytes4(keccak256('balanceOf(address)')) ^ |
||||
bytes4(keccak256('transfer(address,uint256)')) ^ |
||||
bytes4(keccak256('transferFrom(address,address,uint256)')) ^ |
||||
bytes4(keccak256('approve(address,uint256)')) ^ |
||||
bytes4(keccak256('allowance(address,address)')); |
||||
} |
||||
|
||||
/// @title an original cofounder based ERC-20 compliant token |
||||
/// @author Mish Ochu |
||||
/// @dev Ref: https://github.com/ethereum/EIPs/issues/721 |
||||
//http://solidity.readthedocs.io/en/develop/contracts.html#arguments-for-base-constructors |
||||
contract OriginalToken is Cofounded, ERC20, ERC165, InterfaceSignatureConstants { |
||||
bool private hasExecutedCofounderDistribution; |
||||
struct Allowance { |
||||
uint256 amount; |
||||
bool hasBeenPartiallyWithdrawn; |
||||
} |
||||
|
||||
//***** Apparently Optional *****/ |
||||
/// @dev returns the name of the token |
||||
string public constant name = 'Original Crypto Coin'; |
||||
/// @dev returns the symbol of the token (e.g. 'OCC') |
||||
string public constant symbol = 'OCC'; |
||||
/// @dev returns the number of decimals the tokens use |
||||
uint8 public constant decimals = 18; |
||||
//**********/ |
||||
|
||||
/// @dev returns the total token supply |
||||
/// @note implemented as a state variable with an automatic (compiler provided) getter |
||||
/// instead of a constant (view/readonly) function. |
||||
uint256 public totalSupply = 100000000000000000000000000000; |
||||
|
||||
mapping (address => uint256) public balances; |
||||
// TODO: determine if the gas cost for handling the race condition |
||||
// (outlined here: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729) |
||||
// is cheaper this way (or this way: https://github.com/Giveth/minime/blob/master/contracts/MiniMeToken.sol#L221-L225) |
||||
mapping (address => mapping (address => Allowance)) public allowances; |
||||
|
||||
/// @dev creates the token |
||||
/// NOTE passes tokenCofounders to base contract |
||||
/// see Cofounded |
||||
function OriginalToken (address[] tokenCofounders, |
||||
uint256 cofounderDistribution) Cofounded(tokenCofounders) public { |
||||
|
||||
if (hasExecutedCofounderDistribution || |
||||
cofounderDistribution == 0 || |
||||
totalSupply < cofounderDistribution) revert(); |
||||
|
||||
hasExecutedCofounderDistribution = true; |
||||
uint256 initialSupply = totalSupply; |
||||
|
||||
// divvy up initial token supply accross cofounders |
||||
// TODO: ensure each cofounder gets an equal base distribution |
||||
|
||||
for (uint8 x = 0; x < cofounders.length; x++) { |
||||
address cofounder = cofounders[x]; |
||||
|
||||
initialSupply -= cofounderDistribution; |
||||
// there should be some left over for the airdrop campaign |
||||
// otherwise don't create this contract |
||||
if (initialSupply < cofounderDistribution) revert(); |
||||
balances[cofounder] = cofounderDistribution; |
||||
} |
||||
|
||||
balances[msg.sender] += initialSupply; |
||||
} |
||||
|
||||
function transfer (address to, uint256 value) public returns (bool) { |
||||
return transferBalance (msg.sender, to, value); |
||||
} |
||||
|
||||
function transferFrom (address from, address to, uint256 value) public returns (bool success) { |
||||
Allowance storage allowance = allowances[from][msg.sender]; |
||||
if (allowance.amount < value) revert(); |
||||
|
||||
allowance.hasBeenPartiallyWithdrawn = true; |
||||
allowance.amount -= value; |
||||
|
||||
if (allowance.amount == 0) { |
||||
delete allowances[from][msg.sender]; |
||||
} |
||||
|
||||
return transferBalance(from, to, value); |
||||
} |
||||
|
||||
event ApprovalDenied (address indexed owner, address indexed spender); |
||||
|
||||
// TODO: test with an unintialized Allowance struct |
||||
function approve (address spender, uint256 value) public returns (bool success) { |
||||
Allowance storage allowance = allowances[msg.sender][spender]; |
||||
|
||||
if (value == 0) { |
||||
delete allowances[msg.sender][spender]; |
||||
Approval(msg.sender, spender, value); |
||||
return true; |
||||
} |
||||
|
||||
if (allowance.hasBeenPartiallyWithdrawn) { |
||||
delete allowances[msg.sender][spender]; |
||||
ApprovalDenied(msg.sender, spender); |
||||
return false; |
||||
} else { |
||||
allowance.amount = value; |
||||
Approval(msg.sender, spender, value); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
// TODO: compare gas cost estimations between this and https://github.com/ConsenSys/Tokens/blob/master/contracts/eip20/EIP20.sol#L39-L45 |
||||
function transferBalance (address from, address to, uint256 value) private returns (bool) { |
||||
// don't burn these tokens |
||||
if (to == address(0) || from == to) revert(); |
||||
// match spec and emit events on 0 value |
||||
if (value == 0) { |
||||
Transfer(msg.sender, to, value); |
||||
return true; |
||||
} |
||||
|
||||
uint256 senderBalance = balances[from]; |
||||
uint256 receiverBalance = balances[to]; |
||||
if (senderBalance < value) revert(); |
||||
senderBalance -= value; |
||||
receiverBalance += value; |
||||
// overflow check (altough one could use https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/math/SafeMath.sol) |
||||
if (receiverBalance < value) revert(); |
||||
|
||||
balances[from] = senderBalance; |
||||
balances[to] = receiverBalance; |
||||
|
||||
Transfer(from, to, value); |
||||
return true; |
||||
} |
||||
|
||||
|
||||
// TODO: test with an unintialized Allowance struct |
||||
function allowance (address owner, address spender) public constant returns (uint256 remaining) { |
||||
return allowances[owner][spender].amount; |
||||
} |
||||
|
||||
function balanceOf (address owner) public constant returns (uint256 balance) { |
||||
return balances[owner]; |
||||
} |
||||
|
||||
function supportsInterface (bytes4 interfaceID) external constant returns (bool) { |
||||
return ((interfaceID == InterfaceSignature_ERC165) || |
||||
(interfaceID == InterfaceSignature_ERC20) || |
||||
(interfaceID == InterfaceSignature_ERC20_PlusOptions)); |
||||
} |
||||
} |
@ -0,0 +1,349 @@ |
||||
pragma solidity ^0.4.18; |
||||
|
||||
/** |
||||
* @title ERC20Basic |
||||
* @dev Simpler version of ERC20 interface |
||||
* @dev see https://github.com/ethereum/EIPs/issues/179 |
||||
*/ |
||||
contract ERC20Basic { |
||||
uint256 public totalSupply; |
||||
function balanceOf(address who) public view returns (uint256); |
||||
function transfer(address to, uint256 value) public returns (bool); |
||||
event Transfer(address indexed from, address indexed to, uint256 value); |
||||
} |
||||
|
||||
/** |
||||
* @title ERC20 interface |
||||
* @dev see https://github.com/ethereum/EIPs/issues/20 |
||||
*/ |
||||
contract ERC20 is ERC20Basic { |
||||
function allowance(address owner, address spender) public view returns (uint256); |
||||
function transferFrom(address from, address to, uint256 value) public returns (bool); |
||||
function approve(address spender, uint256 value) public returns (bool); |
||||
event Approval(address indexed owner, address indexed spender, uint256 value); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* @title SafeMath |
||||
* @dev Math operations with safety checks that throw on error |
||||
*/ |
||||
library SafeMath { |
||||
function mul(uint256 a, uint256 b) internal pure returns (uint256) { |
||||
if (a == 0) { |
||||
return 0; |
||||
} |
||||
uint256 c = a * b; |
||||
assert(c / a == b); |
||||
return c; |
||||
} |
||||
|
||||
function div(uint256 a, uint256 b) internal pure returns (uint256) { |
||||
// assert(b > 0); // Solidity automatically throws when dividing by 0 |
||||
uint256 c = a / b; |
||||
// assert(a == b * c + a % b); // There is no case in which this doesn't hold |
||||
return c; |
||||
} |
||||
|
||||
function sub(uint256 a, uint256 b) internal pure returns (uint256) { |
||||
assert(b <= a); |
||||
return a - b; |
||||
} |
||||
|
||||
function add(uint256 a, uint256 b) internal pure returns (uint256) { |
||||
uint256 c = a + b; |
||||
assert(c >= a); |
||||
return c; |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* @title Basic token |
||||
* @dev Basic version of StandardToken, with no allowances. |
||||
*/ |
||||
contract BasicToken is ERC20Basic { |
||||
using SafeMath for uint256; |
||||
|
||||
mapping(address => uint256) balances; |
||||
|
||||
/** |
||||
* @dev transfer token for a specified address |
||||
* @param _to The address to transfer to. |
||||
* @param _value The amount to be transferred. |
||||
*/ |
||||
function transfer(address _to, uint256 _value) public returns (bool) { |
||||
require(_to != address(0)); |
||||
require(_value <= balances[msg.sender]); |
||||
|
||||
// SafeMath.sub will throw if there is not enough balance. |
||||
balances[msg.sender] = balances[msg.sender].sub(_value); |
||||
balances[_to] = balances[_to].add(_value); |
||||
Transfer(msg.sender, _to, _value); |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* @dev Gets the balance of the specified address. |
||||
* @param _owner The address to query the the balance of. |
||||
* @return An uint256 representing the amount owned by the passed address. |
||||
*/ |
||||
function balanceOf(address _owner) public view returns (uint256 balance) { |
||||
return balances[_owner]; |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* @title Standard ERC20 token |
||||
* |
||||
* @dev Implementation of the basic standard token. |
||||
* @dev https://github.com/ethereum/EIPs/issues/20 |
||||
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol |
||||
*/ |
||||
contract StandardToken is ERC20, BasicToken { |
||||
|
||||
mapping (address => mapping (address => uint256)) internal allowed; |
||||
|
||||
|
||||
/** |
||||
* @dev Transfer tokens from one address to another |
||||
* @param _from address The address which you want to send tokens from |
||||
* @param _to address The address which you want to transfer to |
||||
* @param _value uint256 the amount of tokens to be transferred |
||||
*/ |
||||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { |
||||
require(_to != address(0)); |
||||
require(_value <= balances[_from]); |
||||
require(_value <= allowed[_from][msg.sender]); |
||||
|
||||
balances[_from] = balances[_from].sub(_value); |
||||
balances[_to] = balances[_to].add(_value); |
||||
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); |
||||
Transfer(_from, _to, _value); |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. |
||||
* |
||||
* Beware that changing an allowance with this method brings the risk that someone may use both the old |
||||
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this |
||||
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: |
||||
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 |
||||
* @param _spender The address which will spend the funds. |
||||
* @param _value The amount of tokens to be spent. |
||||
*/ |
||||
function approve(address _spender, uint256 _value) public returns (bool) { |
||||
allowed[msg.sender][_spender] = _value; |
||||
Approval(msg.sender, _spender, _value); |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* @dev Function to check the amount of tokens that an owner allowed to a spender. |
||||
* @param _owner address The address which owns the funds. |
||||
* @param _spender address The address which will spend the funds. |
||||
* @return A uint256 specifying the amount of tokens still available for the spender. |
||||
*/ |
||||
function allowance(address _owner, address _spender) public view returns (uint256) { |
||||
return allowed[_owner][_spender]; |
||||
} |
||||
|
||||
/** |
||||
* approve should be called when allowed[_spender] == 0. To increment |
||||
* allowed value is better to use this function to avoid 2 calls (and wait until |
||||
* the first transaction is mined) |
||||
* From MonolithDAO Token.sol |
||||
*/ |
||||
function increaseApproval(address _spender, uint _addedValue) public returns (bool) { |
||||
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue); |
||||
Approval(msg.sender, _spender, allowed[msg.sender][_spender]); |
||||
return true; |
||||
} |
||||
|
||||
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) { |
||||
uint oldValue = allowed[msg.sender][_spender]; |
||||
if (_subtractedValue > oldValue) { |
||||
allowed[msg.sender][_spender] = 0; |
||||
} else { |
||||
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); |
||||
} |
||||
Approval(msg.sender, _spender, allowed[msg.sender][_spender]); |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* @title Ownable |
||||
* @dev The Ownable contract has an owner address, and provides basic authorization control |
||||
* functions, this simplifies the implementation of "user permissions". |
||||
*/ |
||||
contract Ownable { |
||||
address public owner; |
||||
|
||||
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); |
||||
|
||||
|
||||
/** |
||||
* @dev The Ownable constructor sets the original `owner` of the contract to the sender |
||||
* account. |
||||
*/ |
||||
function Ownable() public { |
||||
owner = msg.sender; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* @dev Throws if called by any account other than the owner. |
||||
*/ |
||||
modifier onlyOwner() { |
||||
require(msg.sender == owner); |
||||
_; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* @dev Allows the current owner to transfer control of the contract to a newOwner. |
||||
* @param newOwner The address to transfer ownership to. |
||||
*/ |
||||
function transferOwnership(address newOwner) public onlyOwner { |
||||
require(newOwner != address(0)); |
||||
OwnershipTransferred(owner, newOwner); |
||||
owner = newOwner; |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* @title Pausable |
||||
* @dev Base contract which allows children to implement an emergency stop mechanism. |
||||
*/ |
||||
contract Pausable is Ownable { |
||||
event PausePublic(bool newState); |
||||
event PauseOwnerAdmin(bool newState); |
||||
|
||||
bool public pausedPublic = true; |
||||
bool public pausedOwnerAdmin = false; |
||||
|
||||
address public admin; |
||||
|
||||
/** |
||||
* @dev Modifier to make a function callable based on pause states. |
||||
*/ |
||||
modifier whenNotPaused() { |
||||
if(pausedPublic) { |
||||
if(!pausedOwnerAdmin) { |
||||
require(msg.sender == admin || msg.sender == owner); |
||||
} else { |
||||
revert(); |
||||
} |
||||
} |
||||
_; |
||||
} |
||||
|
||||
/** |
||||
* @dev called by the owner to set new pause flags |
||||
* pausedPublic can't be false while pausedOwnerAdmin is true |
||||
*/ |
||||
function pause(bool newPausedPublic, bool newPausedOwnerAdmin) onlyOwner public { |
||||
require(!(newPausedPublic == false && newPausedOwnerAdmin == true)); |
||||
|
||||
pausedPublic = newPausedPublic; |
||||
pausedOwnerAdmin = newPausedOwnerAdmin; |
||||
|
||||
PausePublic(newPausedPublic); |
||||
PauseOwnerAdmin(newPausedOwnerAdmin); |
||||
} |
||||
} |
||||
|
||||
contract PausableToken is StandardToken, Pausable { |
||||
|
||||
function transfer(address _to, uint256 _value) public whenNotPaused returns (bool) { |
||||
return super.transfer(_to, _value); |
||||
} |
||||
|
||||
function transferFrom(address _from, address _to, uint256 _value) public whenNotPaused returns (bool) { |
||||
return super.transferFrom(_from, _to, _value); |
||||
} |
||||
|
||||
function approve(address _spender, uint256 _value) public whenNotPaused returns (bool) { |
||||
return super.approve(_spender, _value); |
||||
} |
||||
|
||||
function increaseApproval(address _spender, uint _addedValue) public whenNotPaused returns (bool success) { |
||||
return super.increaseApproval(_spender, _addedValue); |
||||
} |
||||
|
||||
function decreaseApproval(address _spender, uint _subtractedValue) public whenNotPaused returns (bool success) { |
||||
return super.decreaseApproval(_spender, _subtractedValue); |
||||
} |
||||
} |
||||
|
||||
|
||||
contract ZilliqaToken is PausableToken { |
||||
string public constant name = "Zilliqa"; |
||||
string public constant symbol = "ZIL"; |
||||
uint8 public constant decimals = 12; |
||||
|
||||
modifier validDestination( address to ) |
||||
{ |
||||
require(to != address(0x0)); |
||||
require(to != address(this)); |
||||
_; |
||||
} |
||||
|
||||
function ZilliqaToken( address _admin, uint _totalTokenAmount ) |
||||
{ |
||||
// assign the admin account |
||||
admin = _admin; |
||||
|
||||
// assign the total tokens to zilliqa |
||||
totalSupply = _totalTokenAmount; |
||||
balances[msg.sender] = _totalTokenAmount; |
||||
Transfer(address(0x0), msg.sender, _totalTokenAmount); |
||||
} |
||||
|
||||
function transfer(address _to, uint _value) validDestination(_to) returns (bool) |
||||
{ |
||||
return super.transfer(_to, _value); |
||||
} |
||||
|
||||
function transferFrom(address _from, address _to, uint _value) validDestination(_to) returns (bool) |
||||
{ |
||||
return super.transferFrom(_from, _to, _value); |
||||
} |
||||
|
||||
event Burn(address indexed _burner, uint _value); |
||||
|
||||
function burn(uint _value) returns (bool) |
||||
{ |
||||
balances[msg.sender] = balances[msg.sender].sub(_value); |
||||
totalSupply = totalSupply.sub(_value); |
||||
Burn(msg.sender, _value); |
||||
Transfer(msg.sender, address(0x0), _value); |
||||
return true; |
||||
} |
||||
|
||||
// save some gas by making only one contract call |
||||
function burnFrom(address _from, uint256 _value) returns (bool) |
||||
{ |
||||
assert( transferFrom( _from, msg.sender, _value ) ); |
||||
return burn(_value); |
||||
} |
||||
|
||||
function emergencyERC20Drain( ERC20 token, uint amount ) onlyOwner { |
||||
// owner can drain tokens that are sent here by mistake |
||||
token.transfer( owner, amount ); |
||||
} |
||||
|
||||
event AdminTransferred(address indexed previousAdmin, address indexed newAdmin); |
||||
|
||||
function changeAdmin(address newAdmin) onlyOwner { |
||||
// owner can re-assign the admin |
||||
AdminTransferred(admin, newAdmin); |
||||
admin = newAdmin; |
||||
} |
||||
} |
@ -0,0 +1,17 @@ |
||||
import subprocess |
||||
|
||||
p1 = subprocess.Popen(['python3', './slither_format/tests/test_constable_states.py']) |
||||
p1.wait() |
||||
p2 = subprocess.Popen(['python3', './slither_format/tests/test_constant_function.py']) |
||||
p2.wait() |
||||
p3 = subprocess.Popen(['python3', './slither_format/tests/test_external_function.py']) |
||||
p3.wait() |
||||
p4 = subprocess.Popen(['python3', './slither_format/tests/test_unused_state_vars.py']) |
||||
p4.wait() |
||||
p5 = subprocess.Popen(['python3', './slither_format/tests/test_naming_convention.py']) |
||||
p5.wait() |
||||
p6 = subprocess.Popen(['python3', './slither_format/tests/test_pragma.py']) |
||||
p6.wait() |
||||
p7 = subprocess.Popen(['python3', './slither_format/tests/test_solc_version.py']) |
||||
p7.wait() |
||||
|
@ -0,0 +1,57 @@ |
||||
import unittest |
||||
import subprocess, os, sys |
||||
|
||||
class TestConstableState(unittest.TestCase): |
||||
testDataFile = "const_state_variables.sol" |
||||
testDataDir = "./slither_format/tests/test_data/" |
||||
testFilePath = testDataDir+testDataFile |
||||
|
||||
def setUp(self): |
||||
outFD = open(self.testFilePath+".out","w") |
||||
errFD = open(self.testFilePath+".err","w") |
||||
p = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','constable-states',self.testFilePath], stdout=outFD,stderr=errFD) |
||||
p.wait() |
||||
outFD.close() |
||||
errFD.close() |
||||
|
||||
def tearDown(self): |
||||
p = subprocess.Popen(['rm','-f',self.testFilePath+'.out',self.testFilePath+'.err',self.testFilePath+'.format']) |
||||
p.wait() |
||||
|
||||
def test_constable_states(self): |
||||
outFD = open(self.testFilePath+".out","r") |
||||
outFD_lines = outFD.readlines() |
||||
for i in range(len(outFD_lines)): |
||||
outFD_lines[i] = outFD_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD_lines[0].rstrip(),"Number of Slither results: 6") |
||||
self.assertEqual(outFD_lines[1].rstrip(),"Number of patches: 6") |
||||
self.assertEqual(outFD_lines.count("Detector: constable-states"), 6) |
||||
self.assertEqual(outFD_lines.count("Old string: address public myFriendsAddress = 0xc0ffee254729296a45a3885639AC7E10F9d54979"), 1) |
||||
self.assertEqual(outFD_lines.count("New string: address public constant myFriendsAddress = 0xc0ffee254729296a45a3885639AC7E10F9d54979"), 1) |
||||
self.assertEqual(outFD_lines.count("Location start: 132"), 1) |
||||
self.assertEqual(outFD_lines.count("Location end: 208"), 1) |
||||
self.assertEqual(outFD_lines.count("Old string: uint public test = 5"), 1) |
||||
self.assertEqual(outFD_lines.count("New string: uint public constant test = 5"), 1) |
||||
self.assertEqual(outFD_lines.count("Location start: 237"), 1) |
||||
self.assertEqual(outFD_lines.count("Location end: 257"), 1) |
||||
self.assertEqual(outFD_lines.count("Old string: string text2 = \"xyz\""), 1) |
||||
self.assertEqual(outFD_lines.count("New string: string constant text2 = \"xyz\""), 1) |
||||
self.assertEqual(outFD_lines.count("Location start: 333"), 1) |
||||
self.assertEqual(outFD_lines.count("Location end: 353"), 1) |
||||
self.assertEqual(outFD_lines.count("Old string: address public mySistersAddress = 0x999999cf1046e68e36E1aA2E0E07105eDDD1f08E"), 1) |
||||
self.assertEqual(outFD_lines.count("New string: address public constant mySistersAddress = 0x999999cf1046e68e36E1aA2E0E07105eDDD1f08E"), 1) |
||||
self.assertEqual(outFD_lines.count("Location start: 496"), 1) |
||||
self.assertEqual(outFD_lines.count("Location end: 572"), 1) |
||||
self.assertEqual(outFD_lines.count("Old string: bytes32 should_be_constant = sha256('abc')"), 1) |
||||
self.assertEqual(outFD_lines.count("New string: bytes32 constant should_be_constant = sha256('abc')"), 1) |
||||
self.assertEqual(outFD_lines.count("Location start: 793"), 1) |
||||
self.assertEqual(outFD_lines.count("Location end: 835"), 1) |
||||
self.assertEqual(outFD_lines.count("Old string: uint should_be_constant_2 = A + 1"), 1) |
||||
self.assertEqual(outFD_lines.count("New string: uint constant should_be_constant_2 = A + 1"), 1) |
||||
self.assertEqual(outFD_lines.count("Location start: 841"), 1) |
||||
self.assertEqual(outFD_lines.count("Location end: 874"), 1) |
||||
outFD.close() |
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main() |
@ -0,0 +1,65 @@ |
||||
import unittest |
||||
import subprocess, os, sys |
||||
|
||||
class TestConstantFunctions(unittest.TestCase): |
||||
testDataFile1 = "constant.sol" |
||||
testDataFile2 = "constant-0.5.1.sol" |
||||
testDataDir = "./slither_format/tests/test_data/" |
||||
testFilePath1 = testDataDir+testDataFile1 |
||||
testFilePath2 = testDataDir+testDataFile2 |
||||
|
||||
def setUp(self): |
||||
outFD1 = open(self.testFilePath1+".out","w") |
||||
errFD1 = open(self.testFilePath1+".err","w") |
||||
p1 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','constant-function',self.testFilePath1], stdout=outFD1,stderr=errFD1) |
||||
p1.wait() |
||||
outFD2 = open(self.testFilePath2+".out","w") |
||||
errFD2 = open(self.testFilePath2+".err","w") |
||||
p2 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','constant-function',self.testFilePath2], stdout=outFD2,stderr=errFD2) |
||||
p2.wait() |
||||
outFD1.close() |
||||
errFD1.close() |
||||
outFD2.close() |
||||
errFD2.close() |
||||
|
||||
def tearDown(self): |
||||
p1 = subprocess.Popen(['rm','-f',self.testFilePath1+'.out',self.testFilePath1+'.err',self.testFilePath1+'.format']) |
||||
p1.wait() |
||||
p2 = subprocess.Popen(['rm','-f',self.testFilePath2+'.out',self.testFilePath2+'.err',self.testFilePath2+'.format']) |
||||
p2.wait() |
||||
|
||||
def test_constant_function(self): |
||||
outFD1 = open(self.testFilePath1+".out","r") |
||||
outFD1_lines = outFD1.readlines() |
||||
for i in range(len(outFD1_lines)): |
||||
outFD1_lines[i] = outFD1_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath1+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD1_lines[0],"Number of Slither results: 3") |
||||
self.assertEqual(outFD1_lines[1],"Number of patches: 3") |
||||
self.assertEqual(outFD1_lines.count("Detector: constant-function"), 3) |
||||
self.assertEqual(outFD1_lines.count("Old string: () public view"), 2) |
||||
self.assertEqual(outFD1_lines.count("Old string: () public constant"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: () public"), 3) |
||||
self.assertEqual(outFD1_lines.count("Location start: 67"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 81"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 139"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 157"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 350"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 364"), 1) |
||||
outFD1.close() |
||||
|
||||
outFD2 = open(self.testFilePath2+".out","r") |
||||
outFD2_lines = outFD2.readlines() |
||||
for i in range(len(outFD2_lines)): |
||||
outFD2_lines[i] = outFD2_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath2+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD2_lines[0],"Number of Slither results: 1") |
||||
self.assertEqual(outFD2_lines[1],"Number of patches: 1") |
||||
self.assertEqual(outFD2_lines.count("Old string: () public view"), 1) |
||||
self.assertEqual(outFD2_lines.count("New string: () public"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location start: 211"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location end: 225"), 1) |
||||
outFD2.close() |
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main() |
@ -0,0 +1,52 @@ |
||||
//pragma solidity ^0.4.24; |
||||
|
||||
|
||||
contract A { |
||||
|
||||
address constant public MY_ADDRESS = 0xE0f5206BBD039e7b0592d8918820024e2a7437b9; |
||||
address public myFriendsAddress = 0xc0ffee254729296a45a3885639AC7E10F9d54979; |
||||
|
||||
uint public used; |
||||
uint public test = 5; |
||||
|
||||
uint constant X = 32**22 + 8; |
||||
string constant TEXT1 = "abc"; |
||||
string text2 = "xyz"; |
||||
|
||||
function setUsed() public { |
||||
if (msg.sender == MY_ADDRESS) { |
||||
used = test; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
contract B is A { |
||||
|
||||
address public mySistersAddress = 0x999999cf1046e68e36E1aA2E0E07105eDDD1f08E; |
||||
|
||||
function () external { |
||||
used = 0; |
||||
} |
||||
|
||||
function setUsed(uint a) public { |
||||
if (msg.sender == MY_ADDRESS) { |
||||
used = a; |
||||
} |
||||
} |
||||
} |
||||
|
||||
contract MyConc{ |
||||
|
||||
uint constant A = 1; |
||||
bytes32 should_be_constant = sha256('abc'); |
||||
uint should_be_constant_2 = A + 1; |
||||
address not_constant = msg.sender; |
||||
uint not_constant_2 = getNumber(); |
||||
uint not_constant_3 = 10 + block.number; |
||||
|
||||
function getNumber() public returns(uint){ |
||||
return block.number; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,18 @@ |
||||
contract Constant { |
||||
|
||||
uint a; |
||||
|
||||
|
||||
function test_view_shadow() public view{ |
||||
uint a; |
||||
a = 0; |
||||
} |
||||
|
||||
function test_view() public view{ |
||||
a; |
||||
} |
||||
|
||||
function test_assembly_bug() public view{ |
||||
assembly{} |
||||
} |
||||
} |
@ -0,0 +1,25 @@ |
||||
contract Constant { |
||||
|
||||
uint a; |
||||
|
||||
function test_view_bug() public view{ |
||||
a = 0; |
||||
} |
||||
|
||||
function test_constant_bug() public constant{ |
||||
a = 0; |
||||
} |
||||
|
||||
function test_view_shadow() public view{ |
||||
uint a; |
||||
a = 0; |
||||
} |
||||
|
||||
function test_view() public view{ |
||||
a; |
||||
} |
||||
|
||||
function test_assembly_bug() public view{ |
||||
assembly{} |
||||
} |
||||
} |
@ -0,0 +1,101 @@ |
||||
//pragma solidity ^0.4.24; |
||||
|
||||
import "./external_function_import.sol"; |
||||
|
||||
contract ContractWithFunctionCalledSuper is ContractWithFunctionCalled { |
||||
function callWithSuper() public { |
||||
uint256 i = 0; |
||||
} |
||||
} |
||||
|
||||
contract ContractWithFunctionNotCalled { |
||||
|
||||
modifier mod (uint c) { |
||||
require (c > 0); |
||||
_; |
||||
} |
||||
|
||||
// With parameter and return |
||||
function funcNotCalled5(uint _i) |
||||
public returns (uint) { |
||||
return(_i + 10); |
||||
} |
||||
|
||||
// With no visibility specifier which is ok until solc 0.5.0 |
||||
function funcNotCalled4() { |
||||
} |
||||
|
||||
function funcNotCalled3() public |
||||
returns (uint a) |
||||
{ |
||||
a = 100; |
||||
} |
||||
|
||||
function funcNotCalled2() public { |
||||
} |
||||
|
||||
function funcNotCalled() public { |
||||
} |
||||
|
||||
function my_func() internal returns(bool) { |
||||
return true; |
||||
} |
||||
|
||||
/* Cannot be converted to external because parameter i is written to in function and so cannot be in calldata */ |
||||
function test4(uint i) public returns(uint){ |
||||
i += 1; |
||||
return(i); |
||||
} |
||||
|
||||
/* public with modifier */ |
||||
function test5(uint number) public mod (number) returns(uint){ |
||||
return(number); |
||||
} |
||||
|
||||
/* implicit with modifier */ |
||||
function test6(uint number) mod (number) returns(uint){ |
||||
return(number); |
||||
} |
||||
|
||||
} |
||||
|
||||
contract ContractWithFunctionNotCalled2 is ContractWithFunctionCalledSuper { |
||||
function funcNotCalled() public { |
||||
uint256 i = 0; |
||||
address three = address(new ContractWithFunctionNotCalled()); |
||||
three.call(abi.encode(bytes4(keccak256("helloTwo()")))); |
||||
super.callWithSuper(); |
||||
ContractWithFunctionCalled c = new ContractWithFunctionCalled(); |
||||
c.funcCalled(); |
||||
} |
||||
} |
||||
|
||||
contract InternalCall { |
||||
|
||||
function() returns(uint) ptr; |
||||
|
||||
function set_test1() external{ |
||||
ptr = test1; |
||||
} |
||||
|
||||
function set_test2() external{ |
||||
ptr = test2; |
||||
} |
||||
|
||||
function test1() public returns(uint){ |
||||
return 1; |
||||
} |
||||
|
||||
function test2() public returns(uint){ |
||||
return 2; |
||||
} |
||||
|
||||
function test3() public returns(uint){ |
||||
return 3; |
||||
} |
||||
|
||||
function exec() external returns(uint){ |
||||
return ptr(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,55 @@ |
||||
// This tests against false-positives. This test should output no recommendations from the external-function detector. |
||||
|
||||
|
||||
contract ContractWithBaseFunctionCalled { |
||||
function getsCalledByBase() public; |
||||
function callsOverrideMe() external { |
||||
getsCalledByBase(); |
||||
} |
||||
} |
||||
|
||||
|
||||
contract DerivingContractWithBaseCalled is ContractWithBaseFunctionCalled { |
||||
function getsCalledByBase() public { |
||||
// This should not be recommended to be marked external because it is called by the base class. |
||||
} |
||||
} |
||||
|
||||
|
||||
// All the contracts below should not recommend changing to external since inherited contracts have dynamic calls. |
||||
contract ContractWithDynamicCall { |
||||
function() returns(uint) ptr; |
||||
|
||||
function test1() public returns(uint){ |
||||
return 1; |
||||
} |
||||
|
||||
function test2() public returns(uint){ |
||||
return 2; |
||||
} |
||||
|
||||
function setTest1() external{ |
||||
ptr = test1; |
||||
} |
||||
|
||||
function setTest2() external{ |
||||
ptr = test2; |
||||
} |
||||
|
||||
function exec() external returns(uint){ |
||||
return ptr(); |
||||
} |
||||
} |
||||
|
||||
contract DerivesFromDynamicCall is ContractWithDynamicCall{ |
||||
function getsCalledDynamically() public returns (uint){ |
||||
// This should not be recommended because it is called dynamically. |
||||
return 3; |
||||
} |
||||
function setTest3() public { |
||||
// This should not be recommended because we inherit from a contract that calls dynamically, and we cannot be |
||||
// sure it did not somehow call this function. |
||||
|
||||
ptr = getsCalledDynamically; |
||||
} |
||||
} |
@ -0,0 +1,7 @@ |
||||
// This file is imported by external_function.sol |
||||
|
||||
contract ContractWithFunctionCalled { |
||||
function funcCalled() external { |
||||
uint256 i = 0; |
||||
} |
||||
} |
@ -0,0 +1,108 @@ |
||||
//pragma solidity ^0.4.24; |
||||
|
||||
contract naming { |
||||
|
||||
enum Numbers {ONE, TWO} |
||||
enum numbers {ONE, TWO} |
||||
|
||||
numbers nums = numbers.TWO; |
||||
|
||||
uint constant MY_CONSTANT = 1; |
||||
uint constant MY_other_CONSTANT = 2; |
||||
|
||||
uint Var_One = 1; uint varTwo = 2; |
||||
|
||||
struct test { |
||||
uint a; |
||||
} |
||||
|
||||
struct Test { |
||||
uint a; |
||||
} |
||||
|
||||
test t; |
||||
|
||||
event Event_(uint); |
||||
event event_(uint); |
||||
|
||||
uint Number2; |
||||
|
||||
function getOne(bytes32 b) view public returns(uint) { |
||||
return MY_other_CONSTANT; |
||||
} |
||||
|
||||
function getOne(uint i) view public returns(numbers) { |
||||
numbers num; |
||||
num = numbers.ONE; |
||||
return(num); |
||||
} |
||||
|
||||
function getOne() view public returns(uint) |
||||
{ |
||||
uint naming; |
||||
naming = GetOne(naming); |
||||
event_(naming); |
||||
return 1; |
||||
} |
||||
|
||||
function GetOne(uint i) view public returns (uint) |
||||
{ |
||||
return (1 + Number2); |
||||
} |
||||
|
||||
function setInt(uint number1, uint Number2) public |
||||
{ |
||||
uint i = number1 + Number2; |
||||
} |
||||
|
||||
|
||||
modifier CantDo() { |
||||
_; |
||||
} |
||||
|
||||
modifier canDo() { |
||||
_; |
||||
} |
||||
} |
||||
|
||||
contract Test { |
||||
naming n; |
||||
|
||||
function foo() { |
||||
n.GetOne(10); |
||||
} |
||||
} |
||||
|
||||
contract T { |
||||
uint private _myPrivateVar; |
||||
uint _myPublicVar; |
||||
|
||||
modifier ModifierTest() { |
||||
_; |
||||
} |
||||
|
||||
modifier modifierTestContractTypes(naming m1) { |
||||
naming m2; |
||||
_; |
||||
} |
||||
|
||||
function functionTestModifier(uint i) public ModifierTest returns(uint) { |
||||
return _myPrivateVar; |
||||
} |
||||
|
||||
function functionTestContractTypes(naming n1) public returns(naming) { |
||||
naming n2; |
||||
return(n2); |
||||
} |
||||
|
||||
function test(uint _unused, uint _used) public returns(uint){ |
||||
return _used; |
||||
} |
||||
|
||||
|
||||
uint k = 1; |
||||
|
||||
uint constant M = 1; |
||||
|
||||
uint l = 1; |
||||
} |
@ -0,0 +1,51 @@ |
||||
pragma solidity ^0.4.24; |
||||
|
||||
/* contract definitione */ |
||||
contract one { |
||||
/* contract declaration as state variable */ |
||||
three k; |
||||
|
||||
function foo(uint i) { |
||||
/* contract declaration as local variable */ |
||||
three l; |
||||
l.foo(10); |
||||
k.foo(10); |
||||
} |
||||
} |
||||
|
||||
/* contract definitione */ |
||||
contract Two { |
||||
/* contract declaration as state variable */ |
||||
one m; |
||||
|
||||
function foo() { |
||||
/* contract declaration as local variable */ |
||||
one n; |
||||
n.foo(10); |
||||
m.foo(100); |
||||
} |
||||
|
||||
} |
||||
|
||||
/* contract definitione */ |
||||
contract three { |
||||
/* contract declaration as state variable */ |
||||
Two o; |
||||
|
||||
/* contract as function return value */ |
||||
function foo(uint i) returns (one) { |
||||
/* contract declaration as local variable */ |
||||
Two p; |
||||
p.foo(); |
||||
o.foo(); |
||||
/* new contract object */ |
||||
one r = new one(); |
||||
return(r); |
||||
} |
||||
|
||||
/* contract as function parameter */ |
||||
function foobar(one q) { |
||||
q.foo(10); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,46 @@ |
||||
pragma solidity ^0.4.24; |
||||
|
||||
contract A { |
||||
|
||||
/* enum definition - bad */ |
||||
enum e {ONE, TWO} |
||||
|
||||
/* enum declaration - bad */ |
||||
e e1; |
||||
|
||||
function foo() { |
||||
/* enum use - bad */ |
||||
e1 = e.ONE; |
||||
} |
||||
} |
||||
|
||||
contract B { |
||||
|
||||
/* enum definition - good */ |
||||
enum E {ONE, TWO} |
||||
|
||||
/* enum definition - good */ |
||||
E e1; |
||||
|
||||
function foo() { |
||||
/* enum use - good */ |
||||
e1 = E.ONE; |
||||
} |
||||
|
||||
} |
||||
|
||||
contract C { |
||||
|
||||
/* enum definition - bad */ |
||||
enum e {ONE, TWO} |
||||
|
||||
/* enum declaration - bad */ |
||||
e e1; |
||||
|
||||
/* enum as parameter and return value - bad */ |
||||
function foo(e eA) returns (e) { |
||||
e e2 = eA; |
||||
return (e2); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,34 @@ |
||||
pragma solidity ^0.4.24; |
||||
|
||||
contract One { |
||||
/* event declaration - bad */ |
||||
event e(uint); |
||||
|
||||
function foo(uint i) { |
||||
/* event call - bad */ |
||||
e(i); |
||||
} |
||||
} |
||||
|
||||
contract Two { |
||||
/* event declaration - good */ |
||||
event E(uint); |
||||
|
||||
function foo(uint i) { |
||||
/* event call - good */ |
||||
E(i); |
||||
} |
||||
|
||||
} |
||||
|
||||
contract Three { |
||||
/* event declaration - bad */ |
||||
event e(uint); |
||||
|
||||
function foo(uint i) { |
||||
/* event call with emit - bad */ |
||||
emit e(i); |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,33 @@ |
||||
pragma solidity ^0.4.24; |
||||
|
||||
contract A { |
||||
|
||||
/* function definition - bad */ |
||||
function Foo() { |
||||
/* function call - bad */ |
||||
uint i = Foobar(10); |
||||
} |
||||
|
||||
/* function definition - bad */ |
||||
function Foobar(uint i) returns (uint) { |
||||
return (1+10); |
||||
} |
||||
|
||||
} |
||||
|
||||
contract B { |
||||
/* function definition - good */ |
||||
function foo() { |
||||
/* function call - good */ |
||||
uint i = foobar(10); |
||||
} |
||||
|
||||
/* function definition - good */ |
||||
function foobar(uint i) returns (uint) { |
||||
A a; |
||||
/* function call - bad */ |
||||
return (a.Foobar(10) + i); |
||||
} |
||||
} |
||||
|
||||
|
@ -0,0 +1,47 @@ |
||||
pragma solidity ^0.4.24; |
||||
|
||||
contract A { |
||||
|
||||
/* modifier definition - good */ |
||||
modifier one() { |
||||
_; |
||||
} |
||||
|
||||
/* modifier use - good */ |
||||
function foo() one { |
||||
} |
||||
} |
||||
|
||||
contract B { |
||||
/* modifier definition - bad */ |
||||
modifier One() { |
||||
_; |
||||
} |
||||
|
||||
/* modifier use - bad */ |
||||
function foo () One { |
||||
} |
||||
|
||||
} |
||||
|
||||
contract C { |
||||
|
||||
/* modifier definition - good */ |
||||
modifier one() { |
||||
_; |
||||
} |
||||
|
||||
/* modifier definition - bad */ |
||||
modifier Two() { |
||||
_; |
||||
} |
||||
|
||||
/* modifier uses - good and bad */ |
||||
function foo() one Two returns (uint) { |
||||
/* Local variable with same name as bad modifier name from contract B */ |
||||
uint One; |
||||
return(One); |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,47 @@ |
||||
pragma solidity ^0.4.24; |
||||
|
||||
contract A { |
||||
|
||||
/* parameter declaration - bad */ |
||||
function foo(uint Count) { |
||||
/* parameter use - bad */ |
||||
uint i = Count; |
||||
} |
||||
|
||||
/* parameter declarations - bad */ |
||||
function foobar(uint Count, uint Number) returns (uint) { |
||||
/* parameter declarations - bad */ |
||||
return (Count+Number); |
||||
} |
||||
|
||||
modifier mod (uint c) { |
||||
require (c > 100); |
||||
_; |
||||
} |
||||
|
||||
/* parameter declarations - bad */ |
||||
/* function parameter passed to modifier */ |
||||
function bar(uint Count) mod (Count) returns(uint) { |
||||
/* parameter declarations - bad */ |
||||
return (Count); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
contract B { |
||||
|
||||
mapping(address => uint256) balances; |
||||
|
||||
/* parameter declarations - bad */ |
||||
function bar(address _to, address _from) returns (uint){ |
||||
uint i; |
||||
/* parameter use - bad */ |
||||
i = balances[_to]; |
||||
/* parameter use - bad */ |
||||
balances[_from] = 100; |
||||
return(i); |
||||
} |
||||
} |
||||
|
||||
|
@ -0,0 +1,30 @@ |
||||
pragma solidity ^0.4.24; |
||||
|
||||
contract A { |
||||
/* State variable declaration constant - good */ |
||||
uint constant NUMBER = 100; |
||||
/* State variable declaration private - good */ |
||||
uint private count = 100; |
||||
/* State variable declaration non-constant non-private - good */ |
||||
uint maxnum = 999; |
||||
|
||||
function foo() { |
||||
/* State variable uses - good */ |
||||
uint i = NUMBER + count + maxnum; |
||||
} |
||||
} |
||||
|
||||
contract B { |
||||
/* State variable declaration constant - bad */ |
||||
uint constant number = 100; |
||||
/* State variable declaration private - bad */ |
||||
uint private Count = 100; |
||||
/* State variable declaration non-constant non-private - good */ |
||||
uint Maxnum = 999; |
||||
function foo() { |
||||
/* State variable uses - bad */ |
||||
uint i = number + Count + Maxnum; |
||||
Count += i; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,51 @@ |
||||
pragma solidity ^0.4.24; |
||||
pragma experimental ABIEncoderV2; |
||||
|
||||
contract A { |
||||
|
||||
/* struct definition - bad */ |
||||
struct s { |
||||
uint i; |
||||
} |
||||
|
||||
/* struct declaration - bad */ |
||||
s s1; |
||||
|
||||
function foo() { |
||||
s1.i = 10; |
||||
} |
||||
} |
||||
|
||||
contract B { |
||||
|
||||
/* struct definition - good */ |
||||
struct S { |
||||
uint i; |
||||
} |
||||
|
||||
/* struct definition - good */ |
||||
S s1; |
||||
|
||||
function foo() { |
||||
s1.i = 10; |
||||
} |
||||
|
||||
} |
||||
|
||||
contract C { |
||||
|
||||
/* struct definition - bad */ |
||||
struct s { |
||||
uint i; |
||||
} |
||||
|
||||
/* struct declaration - bad */ |
||||
s s1; |
||||
|
||||
/* struct as parameter and return value - bad */ |
||||
function foo(s sA) returns (s) { |
||||
s1.i = sA.i; |
||||
return (s1); |
||||
} |
||||
} |
||||
|
@ -0,0 +1 @@ |
||||
pragma solidity ^0.4.23; |
@ -0,0 +1,5 @@ |
||||
pragma solidity ^0.4.24; |
||||
|
||||
import "./pragma.0.4.23.sol"; |
||||
|
||||
contract Test{} |
@ -0,0 +1 @@ |
||||
pragma solidity ^0.5.2; |
@ -0,0 +1,5 @@ |
||||
pragma solidity ^0.5.4; |
||||
|
||||
import "./pragma.0.5.2.sol"; |
||||
|
||||
contract Test{} |
@ -0,0 +1,5 @@ |
||||
// The version pragma below should get flagged by the detector |
||||
pragma solidity ^0.4.23; |
||||
contract Contract{ |
||||
|
||||
} |
@ -0,0 +1,5 @@ |
||||
// The version pragma below should get flagged by the detector |
||||
pragma solidity >=0.4.0 <0.6.0; |
||||
contract Contract{ |
||||
|
||||
} |
@ -0,0 +1,5 @@ |
||||
// The version pragma below should get flagged by the detector |
||||
pragma solidity >=0.4.0 <0.4.25; |
||||
contract Contract{ |
||||
|
||||
} |
@ -0,0 +1,5 @@ |
||||
// The version pragma below should get flagged by the detector |
||||
pragma solidity ^0.5.1; |
||||
contract Contract{ |
||||
|
||||
} |
@ -0,0 +1,13 @@ |
||||
//pragma solidity ^0.4.24; |
||||
|
||||
contract A{ |
||||
address unused ; |
||||
address used; |
||||
} |
||||
|
||||
contract B is A{ |
||||
|
||||
function () external{ |
||||
used = address(0); |
||||
} |
||||
} |
@ -0,0 +1,79 @@ |
||||
import unittest |
||||
import subprocess, os, sys |
||||
|
||||
class TestExternalFunctions(unittest.TestCase): |
||||
testDataFile1 = "external_function.sol" |
||||
testDataFile2 = "external_function_2.sol" |
||||
testDataDir = "./slither_format/tests/test_data/" |
||||
testFilePath1 = testDataDir+testDataFile1 |
||||
testFilePath2 = testDataDir+testDataFile2 |
||||
|
||||
def setUp(self): |
||||
outFD1 = open(self.testFilePath1+".out","w") |
||||
errFD1 = open(self.testFilePath1+".err","w") |
||||
p1 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','external-function',self.testFilePath1], stdout=outFD1,stderr=errFD1) |
||||
p1.wait() |
||||
outFD2 = open(self.testFilePath2+".out","w") |
||||
errFD2 = open(self.testFilePath2+".err","w") |
||||
p2 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','external-function',self.testFilePath2], stdout=outFD2,stderr=errFD2) |
||||
p2.wait() |
||||
outFD1.close() |
||||
errFD1.close() |
||||
outFD2.close() |
||||
errFD2.close() |
||||
|
||||
def tearDown(self): |
||||
p1 = subprocess.Popen(['rm','-f',self.testFilePath1+'.out',self.testFilePath1+'.err',self.testFilePath1+'.format']) |
||||
p1.wait() |
||||
p2 = subprocess.Popen(['rm','-f',self.testFilePath2+'.out',self.testFilePath2+'.err',self.testFilePath2+'.format']) |
||||
p2.wait() |
||||
|
||||
def test_external_function(self): |
||||
outFD1 = open(self.testFilePath1+".out","r") |
||||
outFD1_lines = outFD1.readlines() |
||||
for i in range(len(outFD1_lines)): |
||||
outFD1_lines[i] = outFD1_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath1+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD1_lines[0],"Number of Slither results: 9") |
||||
self.assertEqual(outFD1_lines[1],"Number of patches: 8") |
||||
self.assertEqual(outFD1_lines.count("Detector: external-function"), 8) |
||||
self.assertEqual(outFD1_lines.count("Old string: public returns"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: external returns"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 384"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 399"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string:"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: external"), 4) |
||||
self.assertEqual(outFD1_lines.count("Location start: 524"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 525"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: public returns"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: external returns"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 562"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 581"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: public"), 3) |
||||
self.assertEqual(outFD1_lines.count("Old string: public mod"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: external"), 4) |
||||
self.assertEqual(outFD1_lines.count("Location start: 642"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 649"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 685"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 692"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 1022"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 1033"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 1142"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 1147"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: mod"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: external mod"), 2) |
||||
self.assertEqual(outFD1_lines.count("Location start: 1305"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 1312"), 1) |
||||
outFD1.close() |
||||
|
||||
outFD2 = open(self.testFilePath2+".out","r") |
||||
outFD2_lines = outFD2.readlines() |
||||
for i in range(len(outFD2_lines)): |
||||
outFD2_lines[i] = outFD2_lines[i].strip() |
||||
self.assertFalse(os.path.isfile(self.testFilePath2+".format"),"Patched .format file _is_ created?!") |
||||
self.assertEqual(outFD2_lines[0],"Number of Slither results: 0") |
||||
self.assertEqual(outFD2_lines[1],"Number of patches: 0") |
||||
outFD2.close() |
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main() |
@ -0,0 +1,387 @@ |
||||
import unittest |
||||
import subprocess, os, sys |
||||
|
||||
class TestNamingConvention(unittest.TestCase): |
||||
testDataDir = "./slither_format/tests/test_data/" |
||||
testDataFile1 = "naming_convention_contract.sol" |
||||
testDataFile2 = "naming_convention_modifier.sol" |
||||
testDataFile3 = "naming_convention_structure.sol" |
||||
testDataFile4 = "naming_convention_enum.sol" |
||||
testDataFile5 = "naming_convention_event.sol" |
||||
testDataFile6 = "naming_convention_function.sol" |
||||
testDataFile7 = "naming_convention_parameter.sol" |
||||
testDataFile8 = "naming_convention_state_variable.sol" |
||||
testFilePath1 = testDataDir+testDataFile1 |
||||
testFilePath2 = testDataDir+testDataFile2 |
||||
testFilePath3 = testDataDir+testDataFile3 |
||||
testFilePath4 = testDataDir+testDataFile4 |
||||
testFilePath5 = testDataDir+testDataFile5 |
||||
testFilePath6 = testDataDir+testDataFile6 |
||||
testFilePath7 = testDataDir+testDataFile7 |
||||
testFilePath8 = testDataDir+testDataFile8 |
||||
|
||||
def setUp(self): |
||||
outFD1 = open(self.testFilePath1+".out","w") |
||||
errFD1 = open(self.testFilePath1+".err","w") |
||||
p1 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','naming-convention',self.testFilePath1], stdout=outFD1,stderr=errFD1) |
||||
p1.wait() |
||||
outFD1.close() |
||||
errFD1.close() |
||||
|
||||
outFD2 = open(self.testFilePath2+".out","w") |
||||
errFD2 = open(self.testFilePath2+".err","w") |
||||
p2 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','naming-convention',self.testFilePath2], stdout=outFD2,stderr=errFD2) |
||||
p2.wait() |
||||
outFD2.close() |
||||
errFD2.close() |
||||
|
||||
outFD3 = open(self.testFilePath3+".out","w") |
||||
errFD3 = open(self.testFilePath3+".err","w") |
||||
p3 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','naming-convention',self.testFilePath3], stdout=outFD3,stderr=errFD3) |
||||
p3.wait() |
||||
outFD3.close() |
||||
errFD3.close() |
||||
|
||||
outFD4 = open(self.testFilePath4+".out","w") |
||||
errFD4 = open(self.testFilePath4+".err","w") |
||||
p4 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','naming-convention',self.testFilePath4], stdout=outFD4,stderr=errFD4) |
||||
p4.wait() |
||||
outFD4.close() |
||||
errFD4.close() |
||||
|
||||
outFD5 = open(self.testFilePath5+".out","w") |
||||
errFD5 = open(self.testFilePath5+".err","w") |
||||
p5 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','naming-convention',self.testFilePath5], stdout=outFD5,stderr=errFD5) |
||||
p5.wait() |
||||
outFD5.close() |
||||
errFD5.close() |
||||
|
||||
outFD6 = open(self.testFilePath6+".out","w") |
||||
errFD6 = open(self.testFilePath6+".err","w") |
||||
p6 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','naming-convention',self.testFilePath6], stdout=outFD6,stderr=errFD6) |
||||
p6.wait() |
||||
outFD6.close() |
||||
errFD6.close() |
||||
|
||||
outFD7 = open(self.testFilePath7+".out","w") |
||||
errFD7 = open(self.testFilePath7+".err","w") |
||||
p7 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','naming-convention',self.testFilePath7], stdout=outFD7,stderr=errFD7) |
||||
p7.wait() |
||||
outFD7.close() |
||||
errFD7.close() |
||||
|
||||
outFD8 = open(self.testFilePath8+".out","w") |
||||
errFD8 = open(self.testFilePath8+".err","w") |
||||
p8 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','naming-convention',self.testFilePath8], stdout=outFD8,stderr=errFD8) |
||||
p8.wait() |
||||
outFD8.close() |
||||
errFD8.close() |
||||
|
||||
def tearDown(self): |
||||
p1 = subprocess.Popen(['rm','-f',self.testFilePath1+'.out',self.testFilePath1+'.err',self.testFilePath1+'.format']) |
||||
p1.wait() |
||||
p2 = subprocess.Popen(['rm','-f',self.testFilePath2+'.out',self.testFilePath2+'.err',self.testFilePath2+'.format']) |
||||
p2.wait() |
||||
p3 = subprocess.Popen(['rm','-f',self.testFilePath3+'.out',self.testFilePath3+'.err',self.testFilePath3+'.format']) |
||||
p3.wait() |
||||
p4 = subprocess.Popen(['rm','-f',self.testFilePath4+'.out',self.testFilePath4+'.err',self.testFilePath4+'.format']) |
||||
p4.wait() |
||||
p5 = subprocess.Popen(['rm','-f',self.testFilePath5+'.out',self.testFilePath5+'.err',self.testFilePath5+'.format']) |
||||
p5.wait() |
||||
p6 = subprocess.Popen(['rm','-f',self.testFilePath6+'.out',self.testFilePath6+'.err',self.testFilePath6+'.format']) |
||||
p6.wait() |
||||
p7 = subprocess.Popen(['rm','-f',self.testFilePath7+'.out',self.testFilePath7+'.err',self.testFilePath7+'.format']) |
||||
p7.wait() |
||||
p8 = subprocess.Popen(['rm','-f',self.testFilePath8+'.out',self.testFilePath8+'.err',self.testFilePath8+'.format']) |
||||
p8.wait() |
||||
|
||||
def test_naming_convention_contract(self): |
||||
outFD1 = open(self.testFilePath1+".out","r") |
||||
outFD1_lines = outFD1.readlines() |
||||
outFD1.close() |
||||
for i in range(len(outFD1_lines)): |
||||
outFD1_lines[i] = outFD1_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath1+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD1_lines[0],"Number of Slither results: 2") |
||||
self.assertEqual(outFD1_lines[1],"Number of patches: 10") |
||||
self.assertEqual(outFD1_lines.count("Detector: naming-convention (contract definition)"), 2) |
||||
self.assertEqual(outFD1_lines.count("Detector: naming-convention (contract state variable)"), 2) |
||||
self.assertEqual(outFD1_lines.count("Detector: naming-convention (contract function variable)"), 5) |
||||
self.assertEqual(outFD1_lines.count("Detector: naming-convention (contract new object)"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: contract one"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: contract One"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 53"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 65"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: three k"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: Three k"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 117"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 124"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: three l"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: Three l"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 206"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 213"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: one m"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: One m"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 343"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 348"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: one n"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: One n"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 423"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 428"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: contract three"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: contract Three"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 498"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 512"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: one"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: One"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 646"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 649"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: one r = new one()"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: One r = new one()"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 773"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 790"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: one q"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: One q"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 871"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 876"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: new one()"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: new One()"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 781"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 788"), 1) |
||||
|
||||
def test_naming_convention_modifier(self): |
||||
outFD2 = open(self.testFilePath2+".out","r") |
||||
outFD2_lines = outFD2.readlines() |
||||
outFD2.close() |
||||
for i in range(len(outFD2_lines)): |
||||
outFD2_lines[i] = outFD2_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath2+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD2_lines[0],"Number of Slither results: 2") |
||||
self.assertEqual(outFD2_lines[1],"Number of patches: 4") |
||||
self.assertEqual(outFD2_lines.count("Detector: naming-convention (modifier definition)"), 2) |
||||
self.assertEqual(outFD2_lines.count("Detector: naming-convention (modifier uses)"), 2) |
||||
self.assertEqual(outFD2_lines.count("Old string: modifier One"), 1) |
||||
self.assertEqual(outFD2_lines.count("New string: modifier one"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location start: 215"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location end: 227"), 1) |
||||
self.assertEqual(outFD2_lines.count("Old string: () One"), 1) |
||||
self.assertEqual(outFD2_lines.count("New string: () one"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location start: 288"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location end: 295"), 1) |
||||
self.assertEqual(outFD2_lines.count("Old string: modifier Two"), 1) |
||||
self.assertEqual(outFD2_lines.count("New string: modifier two"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location start: 423"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location end: 435"), 1) |
||||
self.assertEqual(outFD2_lines.count("Old string: () one Two returns"), 1) |
||||
self.assertEqual(outFD2_lines.count("New string: () one two returns"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location start: 503"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location end: 522"), 1) |
||||
|
||||
def test_naming_convention_structure(self): |
||||
outFD3 = open(self.testFilePath3+".out","r") |
||||
outFD3_lines = outFD3.readlines() |
||||
outFD3.close() |
||||
for i in range(len(outFD3_lines)): |
||||
outFD3_lines[i] = outFD3_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath3+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD3_lines[0],"Number of Slither results: 2") |
||||
self.assertEqual(outFD3_lines[1],"Number of patches: 6") |
||||
self.assertEqual(outFD3_lines.count("Detector: naming-convention (struct definition)"), 2) |
||||
self.assertEqual(outFD3_lines.count("Detector: naming-convention (struct use)"), 4) |
||||
self.assertEqual(outFD3_lines.count("Old string: struct s { uint i; }"), 2) |
||||
self.assertEqual(outFD3_lines.count("New string: struct S { uint i; }"), 2) |
||||
self.assertEqual(outFD3_lines.count("Location start: 108"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location end: 134"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location start: 434"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location end: 460"), 1) |
||||
self.assertEqual(outFD3_lines.count("Old string: s s1"), 2) |
||||
self.assertEqual(outFD3_lines.count("New string: S s1"), 2) |
||||
self.assertEqual(outFD3_lines.count("Location start: 171"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location end: 175"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location start: 497"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location end: 501"), 1) |
||||
self.assertEqual(outFD3_lines.count("Old string: s sA"), 1) |
||||
self.assertEqual(outFD3_lines.count("New string: S sA"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location start: 570"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location end: 574"), 1) |
||||
self.assertEqual(outFD3_lines.count("Old string: s"), 1) |
||||
self.assertEqual(outFD3_lines.count("New string: S"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location start: 585"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location end: 586"), 1) |
||||
|
||||
def test_naming_convention_enum(self): |
||||
outFD4 = open(self.testFilePath4+".out","r") |
||||
outFD4_lines = outFD4.readlines() |
||||
outFD4.close() |
||||
for i in range(len(outFD4_lines)): |
||||
outFD4_lines[i] = outFD4_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath4+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD4_lines[0],"Number of Slither results: 2") |
||||
self.assertEqual(outFD4_lines[1],"Number of patches: 8") |
||||
self.assertEqual(outFD4_lines.count("Detector: naming-convention (enum definition)"), 2) |
||||
self.assertEqual(outFD4_lines.count("Detector: naming-convention (enum use)"), 6) |
||||
self.assertEqual(outFD4_lines.count("Old string: enum e {ONE, TWO}"), 2) |
||||
self.assertEqual(outFD4_lines.count("New string: enum E {ONE, TWO}"), 2) |
||||
self.assertEqual(outFD4_lines.count("Location start: 73"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location end: 90"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location start: 426"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location end: 443"), 1) |
||||
self.assertEqual(outFD4_lines.count("Old string: e e1"), 2) |
||||
self.assertEqual(outFD4_lines.count("New string: E e1"), 2) |
||||
self.assertEqual(outFD4_lines.count("Location start: 125"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location end: 129"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location start: 478"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location end: 482"), 1) |
||||
self.assertEqual(outFD4_lines.count("Old string: e eA"), 1) |
||||
self.assertEqual(outFD4_lines.count("New string: E eA"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location start: 549"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location end: 553"), 1) |
||||
self.assertEqual(outFD4_lines.count("Old string: e e2 = eA"), 1) |
||||
self.assertEqual(outFD4_lines.count("New string: E e2 = eA"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location start: 573"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location end: 582"), 1) |
||||
self.assertEqual(outFD4_lines.count("Old string: e.ONE"), 1) |
||||
self.assertEqual(outFD4_lines.count("New string: E.ONE"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location start: 186"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location end: 192"), 1) |
||||
|
||||
def test_naming_convention_event(self): |
||||
outFD5 = open(self.testFilePath5+".out","r") |
||||
outFD5_lines = outFD5.readlines() |
||||
outFD5.close() |
||||
for i in range(len(outFD5_lines)): |
||||
outFD5_lines[i] = outFD5_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath5+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD5_lines[0],"Number of Slither results: 2") |
||||
self.assertEqual(outFD5_lines[1],"Number of patches: 4") |
||||
self.assertEqual(outFD5_lines.count("Detector: naming-convention (event definition)"), 2) |
||||
self.assertEqual(outFD5_lines.count("Detector: naming-convention (event calls)"), 2) |
||||
self.assertEqual(outFD5_lines.count("Old string: event e(uint);"), 2) |
||||
self.assertEqual(outFD5_lines.count("New string: event E(uint);"), 2) |
||||
self.assertEqual(outFD5_lines.count("Location start: 75"), 1) |
||||
self.assertEqual(outFD5_lines.count("Location end: 89"), 1) |
||||
self.assertEqual(outFD5_lines.count("Location start: 148"), 1) |
||||
self.assertEqual(outFD5_lines.count("Location end: 152"), 1) |
||||
self.assertEqual(outFD5_lines.count("Old string: e(i)"), 2) |
||||
self.assertEqual(outFD5_lines.count("New string: E(i)"), 2) |
||||
self.assertEqual(outFD5_lines.count("Location start: 148"), 1) |
||||
self.assertEqual(outFD5_lines.count("Location end: 152"), 1) |
||||
self.assertEqual(outFD5_lines.count("Location start: 438"), 1) |
||||
self.assertEqual(outFD5_lines.count("Location end: 442"), 1) |
||||
|
||||
def test_naming_convention_function(self): |
||||
outFD6 = open(self.testFilePath6+".out","r") |
||||
outFD6_lines = outFD6.readlines() |
||||
outFD6.close() |
||||
for i in range(len(outFD6_lines)): |
||||
outFD6_lines[i] = outFD6_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath6+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD6_lines[0],"Number of Slither results: 2") |
||||
self.assertEqual(outFD6_lines[1],"Number of patches: 4") |
||||
self.assertEqual(outFD6_lines.count("Detector: naming-convention (function definition)"), 2) |
||||
self.assertEqual(outFD6_lines.count("Detector: naming-convention (function calls)"), 2) |
||||
self.assertEqual(outFD6_lines.count("Old string: function Foo"), 1) |
||||
self.assertEqual(outFD6_lines.count("New string: function foo"), 1) |
||||
self.assertEqual(outFD6_lines.count("Location start: 76"), 1) |
||||
self.assertEqual(outFD6_lines.count("Location end: 88"), 1) |
||||
self.assertEqual(outFD6_lines.count("Old string: function Foobar"), 1) |
||||
self.assertEqual(outFD6_lines.count("New string: function foobar"), 1) |
||||
self.assertEqual(outFD6_lines.count("Location start: 189"), 1) |
||||
self.assertEqual(outFD6_lines.count("Location end: 204"), 1) |
||||
self.assertEqual(outFD6_lines.count("Old string: Foobar(10)"), 1) |
||||
self.assertEqual(outFD6_lines.count("New string: foobar(10)"), 1) |
||||
self.assertEqual(outFD6_lines.count("Location start: 136"), 1) |
||||
self.assertEqual(outFD6_lines.count("Location end: 146"), 1) |
||||
self.assertEqual(outFD6_lines.count("Old string: a.Foobar(10)"), 1) |
||||
self.assertEqual(outFD6_lines.count("New string: a.foobar(10)"), 1) |
||||
self.assertEqual(outFD6_lines.count("Location start: 516"), 1) |
||||
self.assertEqual(outFD6_lines.count("Location end: 528"), 1) |
||||
|
||||
def test_naming_convention_parameter(self): |
||||
outFD7 = open(self.testFilePath7+".out","r") |
||||
outFD7_lines = outFD7.readlines() |
||||
outFD7.close() |
||||
for i in range(len(outFD7_lines)): |
||||
outFD7_lines[i] = outFD7_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath7+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD7_lines[0],"Number of Slither results: 6") |
||||
self.assertEqual(outFD7_lines[1],"Number of patches: 12") |
||||
self.assertEqual(outFD7_lines.count("Detector: naming-convention (parameter declaration)"), 6) |
||||
self.assertEqual(outFD7_lines.count("Detector: naming-convention (parameter uses)"), 6) |
||||
self.assertEqual(outFD7_lines.count("Old string: uint Count"), 3) |
||||
self.assertEqual(outFD7_lines.count("New string: uint _Count"), 3) |
||||
self.assertEqual(outFD7_lines.count("Location start: 91"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 101"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location start: 215"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 225"), 1) |
||||
self.assertEqual(outFD7_lines.count("Old string: Count"), 3) |
||||
self.assertEqual(outFD7_lines.count("New string: _Count"), 3) |
||||
self.assertEqual(outFD7_lines.count("Location start: 148"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 153"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location start: 308"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 313"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location start: 489"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 499"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location start: 580"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 585"), 1) |
||||
self.assertEqual(outFD7_lines.count("Old string: Count)"), 1) |
||||
self.assertEqual(outFD7_lines.count("New string: _Count)"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location start: 506"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 512"), 1) |
||||
self.assertEqual(outFD7_lines.count("Old string: uint Number"), 1) |
||||
self.assertEqual(outFD7_lines.count("New string: uint _Number"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location start: 227"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 238"), 1) |
||||
self.assertEqual(outFD7_lines.count("Old string: Number"), 1) |
||||
self.assertEqual(outFD7_lines.count("New string: _Number"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location start: 314"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 320"), 1) |
||||
self.assertEqual(outFD7_lines.count("Old string: address _to"), 1) |
||||
self.assertEqual(outFD7_lines.count("New string: address _To"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location start: 708"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 719"), 1) |
||||
self.assertEqual(outFD7_lines.count("Old string: address _from"), 1) |
||||
self.assertEqual(outFD7_lines.count("New string: address _From"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location start: 721"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 734"), 1) |
||||
self.assertEqual(outFD7_lines.count("Old string: _to"), 1) |
||||
self.assertEqual(outFD7_lines.count("New string: _To"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location start: 811"), 1) |
||||
self.assertEqual(outFD7_lines.count("Location end: 814"), 1) |
||||
self.assertEqual(outFD7_lines.count("Old string: _from"), 1, "Index variables of writes are not captured by node._expression_vars_read of Slither") |
||||
self.assertEqual(outFD7_lines.count("New string: _From"), 1) |
||||
|
||||
def test_naming_convention_state_variable(self): |
||||
outFD8 = open(self.testFilePath8+".out","r") |
||||
outFD8_lines = outFD8.readlines() |
||||
outFD8.close() |
||||
for i in range(len(outFD8_lines)): |
||||
outFD8_lines[i] = outFD8_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath8+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD8_lines[0],"Number of Slither results: 3") |
||||
self.assertEqual(outFD8_lines[1],"Number of patches: 7") |
||||
self.assertEqual(outFD8_lines.count("Detector: naming-convention (state variable declaration)"), 3) |
||||
self.assertEqual(outFD8_lines.count("Detector: naming-convention (state variable uses)"), 4) |
||||
self.assertEqual(outFD8_lines.count("Old string: number"), 2) |
||||
self.assertEqual(outFD8_lines.count("New string: NUMBER"), 2) |
||||
self.assertEqual(outFD8_lines.count("Location start: 469"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location end: 475"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location start: 716"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location end: 722"), 1) |
||||
self.assertEqual(outFD8_lines.count("Old string: Count"), 3) |
||||
self.assertEqual(outFD8_lines.count("New string: count"), 3) |
||||
self.assertEqual(outFD8_lines.count("Location start: 547"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location end: 552"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location start: 725"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location end: 730"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location start: 745"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location end: 750"), 1) |
||||
self.assertEqual(outFD8_lines.count("Old string: Maxnum"), 2) |
||||
self.assertEqual(outFD8_lines.count("New string: maxnum"), 2) |
||||
self.assertEqual(outFD8_lines.count("Location start: 634"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location end: 640"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location start: 733"), 1) |
||||
self.assertEqual(outFD8_lines.count("Location end: 739"), 1) |
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main() |
@ -0,0 +1,75 @@ |
||||
import unittest |
||||
import subprocess, os, sys |
||||
|
||||
class TestPragma(unittest.TestCase): |
||||
testDataDir = "./slither_format/tests/test_data/" |
||||
testDataFile1 = "pragma.0.4.24.sol" |
||||
testImportFile1 = "pragma.0.4.23.sol" |
||||
testFilePath1 = testDataDir+testDataFile1 |
||||
testImportFilePath1 = testDataDir+testImportFile1 |
||||
testDataFile2 = "pragma.0.5.4.sol" |
||||
testImportFile2 = "pragma.0.5.2.sol" |
||||
testFilePath2 = testDataDir+testDataFile2 |
||||
testImportFilePath2 = testDataDir+testImportFile2 |
||||
|
||||
def setUp(self): |
||||
outFD1 = open(self.testFilePath1+".out","w") |
||||
errFD1 = open(self.testFilePath1+".err","w") |
||||
p1 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','pragma',self.testFilePath1], stdout=outFD1,stderr=errFD1) |
||||
p1.wait() |
||||
outFD1.close() |
||||
errFD1.close() |
||||
|
||||
outFD2 = open(self.testFilePath2+".out","w") |
||||
errFD2 = open(self.testFilePath2+".err","w") |
||||
my_env = os.environ.copy() |
||||
my_env["SOLC_VERSION"] = "0.5.4" |
||||
p2 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','pragma',self.testFilePath2], stdout=outFD2,stderr=errFD2, env=my_env) |
||||
p2.wait() |
||||
outFD2.close() |
||||
errFD2.close() |
||||
|
||||
def tearDown(self): |
||||
p1 = subprocess.Popen(['rm','-f',self.testFilePath1+'.out',self.testFilePath1+'.err',self.testFilePath1+'.format',self.testImportFilePath1+'.format']) |
||||
p1.wait() |
||||
|
||||
p2 = subprocess.Popen(['rm','-f',self.testFilePath2+'.out',self.testFilePath2+'.err',self.testFilePath2+'.format',self.testImportFilePath2+'.format']) |
||||
p2.wait() |
||||
|
||||
def test_pragma(self): |
||||
outFD1 = open(self.testFilePath1+".out","r") |
||||
outFD1_lines = outFD1.readlines() |
||||
for i in range(len(outFD1_lines)): |
||||
outFD1_lines[i] = outFD1_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath1+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD1_lines[0],"Number of Slither results: 2") |
||||
self.assertEqual(outFD1_lines[1],"Number of patches: 2") |
||||
self.assertEqual(outFD1_lines.count("Detector: pragma"), 2) |
||||
self.assertEqual(outFD1_lines.count("Old string: pragma solidity ^0.4.23;"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: pragma solidity ^0.4.24;"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: pragma solidity 0.4.25;"), 2) |
||||
self.assertEqual(outFD1_lines.count("Location start: 0"), 2) |
||||
self.assertEqual(outFD1_lines.count("Location end: 24"), 2) |
||||
self.assertEqual(outFD1_lines.count("Patch file: ./slither_format/tests/test_data/pragma.0.4.24.sol"), 1) |
||||
self.assertEqual(outFD1_lines.count("Patch file: ./slither_format/tests/test_data/pragma.0.4.23.sol"), 1) |
||||
outFD1.close() |
||||
|
||||
outFD2 = open(self.testFilePath2+".out","r") |
||||
outFD2_lines = outFD2.readlines() |
||||
for i in range(len(outFD2_lines)): |
||||
outFD2_lines[i] = outFD2_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath2+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD2_lines[0],"Number of Slither results: 2") |
||||
self.assertEqual(outFD2_lines[1],"Number of patches: 2") |
||||
self.assertEqual(outFD2_lines.count("Detector: pragma"), 2) |
||||
self.assertEqual(outFD2_lines.count("Old string: pragma solidity ^0.5.4;"), 1) |
||||
self.assertEqual(outFD2_lines.count("Old string: pragma solidity ^0.5.2;"), 1) |
||||
self.assertEqual(outFD2_lines.count("New string: pragma solidity 0.5.3;"), 2) |
||||
self.assertEqual(outFD2_lines.count("Location start: 0"), 2) |
||||
self.assertEqual(outFD2_lines.count("Location end: 23"), 2) |
||||
self.assertEqual(outFD2_lines.count("Patch file: ./slither_format/tests/test_data/pragma.0.5.4.sol"), 1) |
||||
self.assertEqual(outFD2_lines.count("Patch file: ./slither_format/tests/test_data/pragma.0.5.2.sol"), 1) |
||||
outFD2.close() |
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main() |
@ -0,0 +1,118 @@ |
||||
import unittest |
||||
import subprocess, os, sys |
||||
|
||||
class TestSolcVersion(unittest.TestCase): |
||||
testDataDir = "./slither_format/tests/test_data/" |
||||
testDataFile1 = "solc_version_incorrect1.sol" |
||||
testFilePath1 = testDataDir+testDataFile1 |
||||
testDataFile2 = "solc_version_incorrect2.sol" |
||||
testFilePath2 = testDataDir+testDataFile2 |
||||
testDataFile3 = "solc_version_incorrect3.sol" |
||||
testFilePath3 = testDataDir+testDataFile3 |
||||
testDataFile4 = "solc_version_incorrect4.sol" |
||||
testFilePath4 = testDataDir+testDataFile4 |
||||
|
||||
def setUp(self): |
||||
outFD1 = open(self.testFilePath1+".out","w") |
||||
errFD1 = open(self.testFilePath1+".err","w") |
||||
p1 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','solc-version',self.testFilePath1], stdout=outFD1,stderr=errFD1) |
||||
p1.wait() |
||||
outFD1.close() |
||||
errFD1.close() |
||||
|
||||
outFD2 = open(self.testFilePath2+".out","w") |
||||
errFD2 = open(self.testFilePath2+".err","w") |
||||
p2 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','solc-version',self.testFilePath2], stdout=outFD2,stderr=errFD2) |
||||
p2.wait() |
||||
outFD2.close() |
||||
errFD2.close() |
||||
|
||||
outFD3 = open(self.testFilePath3+".out","w") |
||||
errFD3 = open(self.testFilePath3+".err","w") |
||||
p3 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','solc-version',self.testFilePath3], stdout=outFD3,stderr=errFD3) |
||||
p3.wait() |
||||
outFD3.close() |
||||
errFD3.close() |
||||
|
||||
outFD4 = open(self.testFilePath4+".out","w") |
||||
errFD4 = open(self.testFilePath4+".err","w") |
||||
my_env = os.environ.copy() |
||||
my_env["SOLC_VERSION"] = "0.5.2" |
||||
p4 = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','solc-version',self.testFilePath4], stdout=outFD4,stderr=errFD4, env=my_env) |
||||
p4.wait() |
||||
outFD4.close() |
||||
errFD4.close() |
||||
|
||||
def tearDown(self): |
||||
p1 = subprocess.Popen(['rm','-f',self.testFilePath1+'.out',self.testFilePath1+'.err',self.testFilePath1+'.format']) |
||||
p1.wait() |
||||
p2 = subprocess.Popen(['rm','-f',self.testFilePath2+'.out',self.testFilePath2+'.err',self.testFilePath2+'.format']) |
||||
p2.wait() |
||||
p3 = subprocess.Popen(['rm','-f',self.testFilePath3+'.out',self.testFilePath3+'.err',self.testFilePath3+'.format']) |
||||
p3.wait() |
||||
p4 = subprocess.Popen(['rm','-f',self.testFilePath4+'.out',self.testFilePath4+'.err',self.testFilePath4+'.format']) |
||||
p4.wait() |
||||
|
||||
def test_solc_version(self): |
||||
outFD1 = open(self.testFilePath1+".out","r") |
||||
outFD1_lines = outFD1.readlines() |
||||
for i in range(len(outFD1_lines)): |
||||
outFD1_lines[i] = outFD1_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath1+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD1_lines[0],"Number of Slither results: 1") |
||||
self.assertEqual(outFD1_lines[1],"Number of patches: 1") |
||||
self.assertEqual(outFD1_lines.count("Detector: solc-version"), 1) |
||||
self.assertEqual(outFD1_lines.count("Old string: pragma solidity ^0.4.23;"), 1) |
||||
self.assertEqual(outFD1_lines.count("New string: pragma solidity 0.4.25;"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location start: 63"), 1) |
||||
self.assertEqual(outFD1_lines.count("Location end: 87"), 1) |
||||
self.assertEqual(outFD1_lines.count("Patch file: ./slither_format/tests/test_data/solc_version_incorrect1.sol"), 1) |
||||
outFD1.close() |
||||
|
||||
outFD2 = open(self.testFilePath2+".out","r") |
||||
outFD2_lines = outFD2.readlines() |
||||
for i in range(len(outFD2_lines)): |
||||
outFD2_lines[i] = outFD2_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath2+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD2_lines[0],"Number of Slither results: 1") |
||||
self.assertEqual(outFD2_lines[1],"Number of patches: 1") |
||||
self.assertEqual(outFD2_lines.count("Detector: solc-version"), 1) |
||||
self.assertEqual(outFD2_lines.count("Old string: pragma solidity >=0.4.0 <0.6.0;"), 1) |
||||
self.assertEqual(outFD2_lines.count("New string: pragma solidity 0.5.3;"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location start: 63"), 1) |
||||
self.assertEqual(outFD2_lines.count("Location end: 94"), 1) |
||||
self.assertEqual(outFD2_lines.count("Patch file: ./slither_format/tests/test_data/solc_version_incorrect2.sol"), 1) |
||||
outFD2.close() |
||||
|
||||
outFD3 = open(self.testFilePath3+".out","r") |
||||
outFD3_lines = outFD3.readlines() |
||||
for i in range(len(outFD3_lines)): |
||||
outFD3_lines[i] = outFD3_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath3+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD3_lines[0],"Number of Slither results: 1") |
||||
self.assertEqual(outFD3_lines[1],"Number of patches: 1") |
||||
self.assertEqual(outFD3_lines.count("Detector: solc-version"), 1) |
||||
self.assertEqual(outFD3_lines.count("Old string: pragma solidity >=0.4.0 <0.4.25;"), 1) |
||||
self.assertEqual(outFD3_lines.count("New string: pragma solidity 0.4.25;"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location start: 63"), 1) |
||||
self.assertEqual(outFD3_lines.count("Location end: 95"), 1) |
||||
self.assertEqual(outFD3_lines.count("Patch file: ./slither_format/tests/test_data/solc_version_incorrect3.sol"), 1) |
||||
outFD3.close() |
||||
|
||||
outFD4 = open(self.testFilePath4+".out","r") |
||||
outFD4_lines = outFD4.readlines() |
||||
for i in range(len(outFD4_lines)): |
||||
outFD4_lines[i] = outFD4_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath4+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD4_lines[0],"Number of Slither results: 1") |
||||
self.assertEqual(outFD4_lines[1],"Number of patches: 1") |
||||
self.assertEqual(outFD4_lines.count("Detector: solc-version"), 1) |
||||
self.assertEqual(outFD4_lines.count("Old string: pragma solidity ^0.5.1;"), 1) |
||||
self.assertEqual(outFD4_lines.count("New string: pragma solidity 0.5.3;"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location start: 63"), 1) |
||||
self.assertEqual(outFD4_lines.count("Location end: 86"), 1) |
||||
self.assertEqual(outFD4_lines.count("Patch file: ./slither_format/tests/test_data/solc_version_incorrect4.sol"), 1) |
||||
outFD4.close() |
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main() |
@ -0,0 +1,37 @@ |
||||
import unittest |
||||
import subprocess, os, sys |
||||
|
||||
class TestUnusedStateVars(unittest.TestCase): |
||||
testDataFile = "unused_state.sol" |
||||
testDataDir = "./slither_format/tests/test_data/" |
||||
testFilePath = testDataDir+testDataFile |
||||
|
||||
def setUp(self): |
||||
outFD = open(self.testFilePath+".out","w") |
||||
errFD = open(self.testFilePath+".err","w") |
||||
p = subprocess.Popen(['python3', '-m', 'slither_format','--verbose','--detect','unused-state',self.testFilePath], stdout=outFD,stderr=errFD) |
||||
p.wait() |
||||
outFD.close() |
||||
errFD.close() |
||||
|
||||
def tearDown(self): |
||||
p = subprocess.Popen(['rm','-f',self.testFilePath+'.out',self.testFilePath+'.err',self.testFilePath+'.format']) |
||||
p.wait() |
||||
|
||||
def test_unused_state_vars(self): |
||||
outFD = open(self.testFilePath+".out","r") |
||||
outFD_lines = outFD.readlines() |
||||
for i in range(len(outFD_lines)): |
||||
outFD_lines[i] = outFD_lines[i].strip() |
||||
self.assertTrue(os.path.isfile(self.testFilePath+".format"),"Patched .format file is not created?!") |
||||
self.assertEqual(outFD_lines[0].rstrip(),"Number of Slither results: 1") |
||||
self.assertEqual(outFD_lines[1].rstrip(),"Number of patches: 1") |
||||
self.assertEqual(outFD_lines.count("Detector: unused-state"), 1) |
||||
self.assertEqual(outFD_lines.count("Old string: address unused ;"), 1) |
||||
self.assertEqual(outFD_lines.count("New string:"), 1) |
||||
self.assertEqual(outFD_lines.count("Location start: 44"), 1) |
||||
self.assertEqual(outFD_lines.count("Location end: 63"), 1) |
||||
outFD.close() |
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main() |
Loading…
Reference in new issue