Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ For more information, see
* `contract-summary`: [Print a summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#contract-summary)
* `loc`: [Count the total number lines of code (LOC), source lines of code (SLOC), and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP), and test files (TEST).](https://github.com/trailofbits/slither/wiki/Printer-documentation#loc)
* `entry-points`: [Print all the state-changing entry point functions of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#entry-points)
* `entry-points-vars`: [Print all the state-changing entry point functions and their variables of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#entry-points-vars)

### In-Depth Review Printers

Expand Down
33 changes: 17 additions & 16 deletions docs/src/printers/Printer-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,23 @@ Slither allows printing contracts information through its printers.
| 9 | `dominator` | Export the dominator tree of each functions |
| 10 | `echidna` | Export Echidna guiding information |
| 11 | `entry-points` | Print all the state-changing entry point functions of the contracts |
| 12 | `evm` | [Print the evm instructions of nodes in functions](#evm) |
| 13 | `function-id` | [Print the keccak256 signature of the functions](#function-id) |
| 14 | `function-summary` | [Print a summary of the functions](#function-summary) |
| 15 | `halstead` | Computes the Halstead complexity metrics for each contract |
| 16 | `human-summary` | [Print a human-readable summary of the contracts](#human-summary) |
| 17 | `inheritance` | [Print the inheritance relations between contracts](#inheritance) |
| 18 | `inheritance-graph` | [Export the inheritance graph of each contract to a dot file](#inheritance-graph) |
| 19 | `loc` | Count the total number lines of code (LOC), source lines of code (SLOC) |
| 20 | `martin` | Martin agile software metrics (Ca, Ce, I, A, D) |
| 21 | `modifiers` | Print the modifiers called by each function |
| 22 | `not-pausable` | Print functions that do not use whenNotPaused |
| 23 | `require` | [Print the require and assert calls of each function](#require) |
| 24 | `slithir` | [Print the slithIR representation of the functions](#slithir) |
| 25 | `slithir-ssa` | [Print the slithIR representation of the functions](#slithir-ssa) |
| 26 | `variable-order` | [Print the storage order of the state variables](#variable-order) |
| 27 | `vars-and-auth` | [Print the state variables written and the authorization of the functions](#variables-written-and-authorization) |
| 12 | `entry-points-vars` | Print all the state-changing entry point functions and their variables of the contracts |
| 13 | `evm` | [Print the evm instructions of nodes in functions](#evm) |
| 14 | `function-id` | [Print the keccak256 signature of the functions](#function-id) |
| 15 | `function-summary` | [Print a summary of the functions](#function-summary) |
| 16 | `halstead` | Computes the Halstead complexity metrics for each contract |
| 17 | `human-summary` | [Print a human-readable summary of the contracts](#human-summary) |
| 18 | `inheritance` | [Print the inheritance relations between contracts](#inheritance) |
| 19 | `inheritance-graph` | [Export the inheritance graph of each contract to a dot file](#inheritance-graph) |
| 20 | `loc` | Count the total number lines of code (LOC), source lines of code (SLOC) |
| 21 | `martin` | Martin agile software metrics (Ca, Ce, I, A, D) |
| 22 | `modifiers` | Print the modifiers called by each function |
| 23 | `not-pausable` | Print functions that do not use whenNotPaused |
| 24 | `require` | [Print the require and assert calls of each function](#require) |
| 25 | `slithir` | [Print the slithIR representation of the functions](#slithir) |
| 26 | `slithir-ssa` | [Print the slithIR representation of the functions](#slithir-ssa) |
| 27 | `variable-order` | [Print the storage order of the state variables](#variable-order) |
| 28 | `vars-and-auth` | [Print the state variables written and the authorization of the functions](#variables-written-and-authorization) |

Several printers require xdot installed for visualization:

Expand Down
1 change: 1 addition & 0 deletions slither/printers/all_printers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@
from .summary.martin import Martin
from .summary.cheatcodes import CheatcodePrinter
from .summary.entry_points import PrinterEntryPoints
from .summary.entry_points_vars import PrinterEntryPointsVars
160 changes: 160 additions & 0 deletions slither/printers/summary/entry_points_vars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
"""
Module printing all the state-changing entry point functions and their variables of the contracts
"""

from pathlib import Path
from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.function_contract import FunctionContract
from slither.utils.colors import Colors
from slither.utils.output import Output
from slither.utils.myprettytable import MyPrettyTable
from slither.utils.tests_pattern import is_test_file


class PrinterEntryPointsVars(AbstractPrinter):

ARGUMENT = "entry-points-vars"
HELP = "Print all the state-changing entry point functions and their variables of the contracts"

WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#entry-points-vars"

def output(self, _filename) -> Output:

Check warning on line 21 in slither/printers/summary/entry_points_vars.py

View workflow job for this annotation

GitHub Actions / Lint Code Base

R0914: Too many local variables (21/15) (too-many-locals)
"""
_filename is not used
Args:
_filename(string)
"""
all_contracts = []

for contract in sorted(
(
c
for c in self.slither.contracts_derived
if not c.is_test
and not c.is_from_dependency()
and not is_test_file(Path(c.source_mapping.filename.absolute))
and not c.is_interface
and not c.is_library
and not c.is_abstract
),
key=lambda x: x.name,
):
entry_points = [
f
for f in contract.functions
if (
f.visibility in ["public", "external"]
and isinstance(f, FunctionContract)
and not f.view
and not f.pure
and not f.is_shadowed
)
]

if not entry_points:
continue

# Build inheritance chain
inheritance_chain = self._get_inheritance_chain(contract)
inheritance_str = f" is {' is '.join(inheritance_chain)}" if inheritance_chain else ""

contract_info = [
f"\n\nContract {Colors.BOLD}{Colors.YELLOW}{contract.name}{Colors.END}{inheritance_str}"
f" ({contract.source_mapping})"
]

# Create variables table
variables_with_slots = self._get_variables_with_slots(contract)
if variables_with_slots:
var_table = MyPrettyTable(["Variables", "Types", "Storage Slots", "Inherited From"])
for var_name, var_type, slot, inherited_from in variables_with_slots:
var_table.add_row(
[
f"{Colors.BOLD}{Colors.BLUE}{var_name}{Colors.END}",
f"{Colors.GREEN}{var_type}{Colors.END}",
f"{Colors.YELLOW}{slot}{Colors.END}",
(
f"{Colors.MAGENTA}{inherited_from}{Colors.END}"
if inherited_from
else ""
),
]
)
contract_info.append(str(var_table))

# Create functions table
func_table = MyPrettyTable(["Functions", "Modifiers", "Inherited From"])

for f in sorted(
entry_points,
key=lambda x, contract=contract: (
x.contract_declarer != contract,
x.contract_declarer.name if x.contract_declarer != contract else "",
x.source_mapping.start,
),
):
name_parts = f.full_name.split("(", 1)
inherited = f.contract_declarer.name if f.contract_declarer != contract else ""
modifiers = ", ".join(
[m.name for m in f.modifiers] + (["payable"] if f.payable else [])
)

# Style special functions differently
if f.is_constructor or f.name in ["receive", "fallback"]:
function_color = f"{Colors.BOLD}{Colors.MAGENTA}"
else:
function_color = f"{Colors.BOLD}{Colors.RED}"

function_name = name_parts[0]

func_table.add_row(
[
f"{function_color}{function_name}{Colors.END}({name_parts[1]}",
f"{Colors.GREEN}{modifiers}{Colors.END}" if modifiers else "",
f"{Colors.MAGENTA}{inherited}{Colors.END}" if inherited else "",
]
)

contract_info.append(str(func_table))
all_contracts.append("\n".join(contract_info))

info = "\n".join(all_contracts) if all_contracts else ""
self.info(info)
return self.generate_output(info)

def _get_inheritance_chain(self, contract):
"""Get the full inheritance chain for a contract"""
inheritance_chain = []
for base in contract.inheritance:
if not base.is_interface and not base.is_library:
inheritance_chain.append(base.name)
return inheritance_chain

def _get_variables_with_slots(self, contract):
"""Get all state variables with their storage slots, types, and inheritance info"""
variables_info = []
slot = 0

# Get all variables including inherited ones, in declaration order
all_variables = []

# First add inherited variables (in reverse inheritance order)
for base_contract in reversed(contract.inheritance):
for var in base_contract.state_variables_declared:
if not var.is_constant and not var.is_immutable:
all_variables.append((var, base_contract.name))

# Then add contract's own variables
for var in contract.state_variables_declared:
if not var.is_constant and not var.is_immutable:
all_variables.append((var, ""))

# Calculate slots for each variable
for var, inherited_from in all_variables:
var_type = str(var.type)
variables_info.append((var.name, var_type, str(slot), inherited_from))
# For simplicity, assume each variable takes 1 slot
# In reality, this would depend on the variable type
slot += 1

return sorted(variables_info, key=lambda x: int(x[2]))