Skip to content

Commit 7a02a00

Browse files
committed
Update slither doc
1 parent 270728e commit 7a02a00

File tree

10 files changed

+245
-29
lines changed

10 files changed

+245
-29
lines changed

.github/workflows/slither.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ jobs:
2929
- name: Install dependencies
3030
run: |
3131
pip install solc-select
32-
solc-select install 0.5.11
33-
solc-select use 0.5.11
3432
- name: Run Tests
3533
run: |
3634
bash program-analysis/slither/scripts/gh_action_test.sh

program-analysis/slither/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ Once you feel confident with the material in this README, proceed to the exercis
1111

1212
- [Exercise 1](./exercise1.md): Function override protection
1313
- [Exercise 2](./exercise2.md): Check for access controls
14+
- [Exercise 3](./exercise3.md): Find variable used in conditional statements
1415

15-
Watch Slither's [code walkthrough](https://www.youtube.com/watch?v=EUl3UlYSluU) to learn about its code structure.
16+
Watch Slither's [code walkthrough](https://www.youtube.com/watch?v=EUl3UlYSluU), or [API walkthrough](https://www.youtube.com/watch?v=Ijf0pellvgw) to learn about its code structure.
1617

1718
## Installation
1819

program-analysis/slither/api.md

Lines changed: 178 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,197 @@
1-
## API Basics
1+
# API Basics
22

33
Slither has an API that allows you to explore basic attributes of contracts and their functions.
44

5+
On a high level there are 6 layers:
6+
- `Slither` - main slither object
7+
- `SlitherCompilationUnit` - group of files used by one call to solc
8+
- `Contract` - contract level
9+
- `Function` - function level
10+
- `Node` - control flow graph
11+
- `SlithrIR` - intermediate representation
12+
13+
Watch our [API walkthrough](https://www.youtube.com/watch?v=Ijf0pellvgw) for more details
14+
15+
## Slither object
16+
517
To load a codebase:
618

719
```python
820
from slither import Slither
921
slither = Slither('/path/to/project')
1022
```
1123

12-
### Exploring Contracts and Functions
24+
To load a contract deployed:
25+
26+
```python
27+
from slither import Slither
28+
slither = Slither('0x..') # assuming the code is verified on etherscan
29+
```
30+
31+
Use `etherscan_api_key` to provide an [Etherscan API KEY](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics)
32+
```python
33+
slither = Slither('0x..', etherscan_api_key='..')
34+
```
35+
36+
You can retrieve the list of compilation units with:
37+
- `sl.compilation_units # array of SlitherCompilationUnit`
38+
39+
40+
## SlitherCompilationUnit object
41+
- ~ group of files used by one call to solc
42+
- Most targets have 1 compilation, but not always true
43+
- Partial compilation for optimization
44+
- Multiple solc version used
45+
- Etc..
46+
- Why compilation unit matters?
47+
- Some APIs might be not intuitive
48+
- Ex: looking for a contract based on the name?
49+
- Can have multiple contracts
50+
- For hacking you can (probably) use the first compilation unit
51+
- `compilation_unit = sl.compilation_units[0]`
52+
1353

14-
A `Slither` object has:
54+
A [`SlitherCompilationUnit`](https://github.com/crytic/slither/blob/master/slither/core/compilation_unit.py) has:
1555

1656
- `contracts (list(Contract))`: A list of contracts
1757
- `contracts_derived (list(Contract))`: A list of contracts that are not inherited by another contract (a subset of contracts)
1858
- `get_contract_from_name (str)`: Returns a list of contracts matching the name
59+
- `[structures | enums | events | variables | functions]_top_level`: Top level object
60+
61+
Example
62+
```python
63+
from slither import Slither
64+
sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7")
65+
compilation_unit = sl.compilation_units[0]
66+
67+
# Print all the contracts from the USDT address
68+
print([str(c) for c in compilation_unit.contracts])
69+
70+
# Print the most derived contracts from the USDT address
71+
print([str(c) for c in compilation_unit.contracts_derived])
72+
```
73+
```bash
74+
% python test.py
75+
['SafeMath', 'Ownable', 'ERC20Basic', 'ERC20', 'BasicToken', 'StandardToken', 'Pausable', 'BlackList', 'UpgradedStandardToken', 'TetherToken']
76+
77+
['SafeMath', 'UpgradedStandardToken', 'TetherToken']
78+
```
79+
80+
## Contract Object
81+
A [`Contract`](https://github.com/crytic/slither/blob/master/slither/core/declarations/contract.py) object has:
82+
83+
- `name: str`: The name of the contract
84+
- `functions: list[Function]`: A list of functions
85+
- `modifiers: list[Modifier]`: A list of modifiers
86+
- `all_functions_called: list[Function/Modifier]`: A list of all internal functions reachable by the contract
87+
- `inheritance: list[Contract]`: A list of inherited contracts (c3 linearization order)
88+
- `derived_contracts: list[Contract]`: contracts derived from it
89+
- `get_function_from_signature(str): Function`: Returns a Function from its signature
90+
- `get_modifier_from_signature(str): Modifier`: Returns a Modifier from its signature
91+
- `get_state_variable_from_name(str): StateVariable`: Returns a StateVariable from its name
92+
- `state_variables: List[StateVariable]`: list of accessible variables
93+
- `state_variables_ordered: List[StateVariable]`: all variable ordered by declaration
94+
95+
Example
96+
```python
97+
from slither import Slither
98+
sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7")
99+
compilation_unit = sl.compilation_units[0]
100+
101+
# Print all the state variables of the USDT token
102+
contract = compilation_unit.get_contract_from_name("TetherToken")[0]
103+
print([str(v) for v in contract.state_variables])
104+
```
105+
106+
```bash
107+
% python test.py
108+
['owner', 'paused', '_totalSupply', 'balances', 'basisPointsRate', 'maximumFee', 'allowed', 'MAX_UINT', 'isBlackListed', 'name', 'symbol', 'decimals', 'upgradedAddress', 'deprecated']
109+
```
110+
111+
## Function object
112+
113+
A [`Function`](https://github.com/crytic/slither/blob/master/slither/core/declarations/function.py) or a `Modifier` object has:
114+
115+
- `name: str`: The name of the function
116+
- `contract: Contract`: The contract where the function is declared
117+
- `nodes: list[Node]`: A list of nodes composing the CFG of the function/modifier
118+
- `entry_point: Node`: The entry point of the CFG
119+
- `[state |local]_variable_[read |write]: list[StateVariable]`: A list of local/state variables read/write
120+
- All can be prefixed by “all_” for recursive lookup
121+
- Ex: `all_state_variable_read`: return all the state variables read in internal calls
122+
- `slithir_operations: List[Operation]`: list of IR operations
123+
124+
```python
125+
from slither import Slither
126+
sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7")
127+
compilation_unit = sl.compilation_units[0]
128+
contract = compilation_unit.get_contract_from_name("TetherToken")[0]
129+
130+
transfer = contract.get_function_from_signature("transfer(address,uint256)")
131+
132+
# Print all the state variables read by the transfer function
133+
print([str(v) for v in transfer.state_variables_read])
134+
# Print all the state variables read by the transfer function and its internal calls
135+
print([str(v) for v in transfer.all_state_variables_read])
136+
```
137+
138+
```bash
139+
% python test.py
140+
['deprecated', 'isBlackListed', 'upgradedAddress']
141+
['owner', 'basisPointsRate', 'deprecated', 'paused', 'isBlackListed', 'maximumFee', 'upgradedAddress', 'balances']
142+
```
19143

20-
A `Contract` object has:
21-
22-
- `name (str)`: The name of the contract
23-
- `functions (list(Function))`: A list of functions
24-
- `modifiers (list(Modifier))`: A list of modifiers
25-
- `all_functions_called (list(Function/Modifier))`: A list of all internal functions reachable by the contract
26-
- `inheritance (list(Contract))`: A list of inherited contracts
27-
- `get_function_from_signature (str)`: Returns a Function from its signature
28-
- `get_modifier_from_signature (str)`: Returns a Modifier from its signature
29-
- `get_state_variable_from_name (str)`: Returns a StateVariable from its name
30-
31-
A `Function` or a `Modifier` object has:
32-
33-
- `name (str)`: The name of the function
34-
- `contract (contract)`: The contract where the function is declared
35-
- `nodes (list(Node))`: A list of nodes composing the CFG of the function/modifier
36-
- `entry_point (Node)`: The entry point of the CFG
37-
- `variables_read (list(Variable))`: A list of variables read
38-
- `variables_written (list(Variable))`: A list of variables written
39-
- `state_variables_read (list(StateVariable))`: A list of state variables read (a subset of `variables_read`)
40-
- `state_variables_written (list(StateVariable))`: A list of state variables written (a subset of `variables_written`)
144+
## Node object
145+
[Node](https://github.com/crytic/slither/blob/master/slither/core/cfg/node.py)
146+
147+
To explore the nodes:
148+
- If order does not matter
149+
- `for node in function.nodes`
150+
- If order matters, walk through the nodes
151+
```python
152+
def visit_node(node: Node, visited: List[Node]):
153+
154+
if node in visited:
155+
return
156+
visited += [node]
157+
158+
# custom action
159+
for son in node.sons:
160+
visit_node(son, visited)
161+
```
162+
- If need to iterate more than once (advanced usages)
163+
- Bound the iteration X times
164+
- Create a fix-point - abstract interpretation style analysis
165+
166+
167+
## SlithIR
168+
- [slither/slithir](https://github.com/crytic/slither/tree/master/slither/slithir)
169+
- Every IR operation has its own methods
170+
- Check if an operation is of a type:
171+
- `isinstance(ir, TYPE)`
172+
- Ex: `isinstance(ir, Call)`
173+
- Check if the operation is an addition
174+
- `isinstance(ir, Binary) & ir.type == BinaryType.ADDITION`
175+
- Check if the operation is a call to MyContract
176+
- `isinstance(ir, HighLevelCall) & ir.destination == MyContract`
177+
178+
```python
179+
from slither import Slither
180+
sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7")
181+
compilation_unit = sl.compilation_units[0]
182+
contract = compilation_unit.get_contract_from_name("TetherToken")[0]
183+
totalSupply = contract.get_function_from_signature("totalSupply()")
184+
185+
# Print the external call made in the totalSupply function
186+
for ir in totalSupply.slithir_operations:
187+
if isinstance(ir, HighLevelCall):
188+
print(f"External call found {ir} ({ir.node.source_mapping})")
189+
```
190+
191+
```bash
192+
% python test.py
193+
External call found HIGH_LEVEL_CALL, […] (...TetherToken.sol#339)
194+
```
41195

42196
### Example: Print Basic Information
43197

program-analysis/slither/exercise1.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Exercise 1: Function Overridden Protection
22

3-
The goal is to create a script that fills in a missing feature of Solidity: function overriding protection.
3+
The goal is to create a script that fills in a solidity feature of Solidity that were not present in previous version of Solidity: function overriding protection.
44

55
[exercises/exercise1/coin.sol](exercises/exercise1/coin.sol) contains a function that must never be overridden:
66

@@ -10,6 +10,8 @@ _mint(address dst, uint256 val)
1010

1111
Use Slither to ensure that no contract inheriting Coin overrides this function.
1212

13+
Use `solc-select install 0.5.0 && solc-select use 0.5.0` to switch to solc 0.5.0
14+
1315
## Proposed Algorithm
1416

1517
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Exercise 3: Find function that use a given variable in a condition
2+
3+
The [exercises/exercise3/find.sol](exercises/exercise3/find.sol) file contains a contract that use `my_variable` variable in multiple locations.
4+
5+
Our goal is to create a script that list all the functions that use `my_variable` in a conditional or require statement.
6+
7+
## Proposed Approach
8+
9+
Explore all the helpers provided by [`Function`](https://github.com/crytic/slither/blob/master/slither/core/declarations/function.py) object to find an easy way to reach the goal
10+
11+
12+
## Solution
13+
14+
Refer to [exercises/exercise3/solution.py](exercises/exercise3/solution.py) for the solution.

program-analysis/slither/exercises/exercise2/coin.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: AGPL-3.0
2-
pragma solidity ^0.5.0;
2+
pragma solidity ^0.8.0;
33

44
contract Owned {
55
address public owner;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The function using "a" in condition are ['condition', 'call_require']
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from slither.slither import Slither
2+
3+
slither = Slither('find.sol')
4+
find = slither.get_contract_from_name('Find')[0]
5+
6+
assert find
7+
8+
# Get the variable
9+
my_variable = find.get_state_variable_from_name("my_variable")
10+
assert my_variable
11+
12+
13+
function_using_a_as_condition = [
14+
f
15+
for f in find.functions
16+
if f.is_reading_in_conditional_node(my_variable) or f.is_reading_in_require_or_assert(my_variable)
17+
]
18+
19+
# Print the result
20+
print(f'The function using "a" in condition are {[f.name for f in function_using_a_as_condition]}')
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
contract Find{
2+
3+
uint my_variable;
4+
5+
function condition() public{
6+
if(my_variable==0){
7+
8+
}
9+
}
10+
11+
function call_require() public{
12+
require(my_variable==0);
13+
}
14+
15+
function read_and_write() public{
16+
my_variable = my_variable + 1;
17+
}
18+
19+
}

program-analysis/slither/scripts/gh_action_test.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,15 @@ cd program-analysis/slither
5757
pip install slither-analyzer
5858

5959
test_examples
60+
solc-select install 0.5.11
61+
solc-select use 0.5.11
6062
test_exercise 1
63+
64+
solc-select install 0.8.20
65+
solc-select use 0.8.20
6166
test_exercise 2
6267

68+
test_exercise 3
69+
6370
echo "Slither tests passed"
6471

0 commit comments

Comments
 (0)