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.

pull/238/head
rajeevgopalakrishna 6 years ago
parent 9341166e9a
commit 106928f911
  1. 1
      utils/slither_format/.gitignore
  2. 0
      utils/slither_format/__init__.py
  3. 51
      utils/slither_format/__main__.py
  4. 25
      utils/slither_format/format_constable_states.py
  5. 37
      utils/slither_format/format_constant_function.py
  6. 59
      utils/slither_format/format_external_function.py
  7. 578
      utils/slither_format/format_naming_convention.py
  8. 69
      utils/slither_format/format_pragma.py
  9. 55
      utils/slither_format/format_solc_version.py
  10. 19
      utils/slither_format/format_unused_state.py
  11. 132
      utils/slither_format/slither_format.py
  12. 1
      utils/slither_format/tests/.gitignore
  13. 332
      utils/slither_format/tests/real_world/0x0000000000b3F879cb30FE243b4Dfee438691c04_GasToken2.sol
  14. 880
      utils/slither_format/tests/real_world/0x006bea43baa3f7a6f765f14f10a1a1b08334ef45_StoxSmartToken.sol
  15. 241
      utils/slither_format/tests/real_world/0x0235fe624e044a05eed7a43e16e3083bc8a4287a_OriginalToken.sol
  16. 349
      utils/slither_format/tests/real_world/0x05f4a42e251f2d52b8ed15e9fedaacfcef1fad27_ZilliqaToken.sol
  17. 17
      utils/slither_format/tests/run_all_tests.py
  18. 57
      utils/slither_format/tests/test_constable_states.py
  19. 65
      utils/slither_format/tests/test_constant_function.py
  20. 52
      utils/slither_format/tests/test_data/const_state_variables.sol
  21. 18
      utils/slither_format/tests/test_data/constant-0.5.1.sol
  22. 25
      utils/slither_format/tests/test_data/constant.sol
  23. 101
      utils/slither_format/tests/test_data/external_function.sol
  24. 55
      utils/slither_format/tests/test_data/external_function_2.sol
  25. 7
      utils/slither_format/tests/test_data/external_function_import.sol
  26. 108
      utils/slither_format/tests/test_data/naming_convention.sol
  27. 51
      utils/slither_format/tests/test_data/naming_convention_contract.sol
  28. 46
      utils/slither_format/tests/test_data/naming_convention_enum.sol
  29. 34
      utils/slither_format/tests/test_data/naming_convention_event.sol
  30. 33
      utils/slither_format/tests/test_data/naming_convention_function.sol
  31. 47
      utils/slither_format/tests/test_data/naming_convention_modifier.sol
  32. 47
      utils/slither_format/tests/test_data/naming_convention_parameter.sol
  33. 30
      utils/slither_format/tests/test_data/naming_convention_state_variable.sol
  34. 51
      utils/slither_format/tests/test_data/naming_convention_structure.sol
  35. 1
      utils/slither_format/tests/test_data/pragma.0.4.23.sol
  36. 5
      utils/slither_format/tests/test_data/pragma.0.4.24.sol
  37. 1
      utils/slither_format/tests/test_data/pragma.0.5.2.sol
  38. 5
      utils/slither_format/tests/test_data/pragma.0.5.4.sol
  39. 5
      utils/slither_format/tests/test_data/solc_version_incorrect1.sol
  40. 5
      utils/slither_format/tests/test_data/solc_version_incorrect2.sol
  41. 5
      utils/slither_format/tests/test_data/solc_version_incorrect3.sol
  42. 5
      utils/slither_format/tests/test_data/solc_version_incorrect4.sol
  43. 13
      utils/slither_format/tests/test_data/unused_state.sol
  44. 79
      utils/slither_format/tests/test_external_function.py
  45. 387
      utils/slither_format/tests/test_naming_convention.py
  46. 75
      utils/slither_format/tests/test_pragma.py
  47. 118
      utils/slither_format/tests/test_solc_version.py
  48. 37
      utils/slither_format/tests/test_unused_state_vars.py

@ -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,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,5 @@
pragma solidity ^0.4.24;
import "./pragma.0.4.23.sol";
contract Test{}

@ -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…
Cancel
Save