mirror of https://github.com/crytic/slither
commit
5802174562
@ -1,41 +1,48 @@ |
||||
name: Bug Report |
||||
description: File a bug report |
||||
title: "[Bug]: " |
||||
labels: ["bug"] |
||||
--- |
||||
body: |
||||
- type: markdown |
||||
- |
||||
attributes: |
||||
value: | |
||||
Please check the issues tab to avoid duplicates. |
||||
|
||||
Thanks for taking the time to fill out this bug report! |
||||
- type: textarea |
||||
id: what-happened |
||||
type: markdown |
||||
- |
||||
attributes: |
||||
label: What happened? |
||||
description: Also tell us, what did you expect to happen? |
||||
value: "A bug happened!" |
||||
label: "Describe the issue:" |
||||
id: what-happened |
||||
type: textarea |
||||
validations: |
||||
required: true |
||||
- type: textarea |
||||
id: reproduce |
||||
- |
||||
attributes: |
||||
label: Can you share code with us to reproduce this bug? |
||||
description: It can be a github repo, etherscan link, or code snippet. |
||||
value: "contract A {}" |
||||
description: "It can be a github repo, etherscan link, or code snippet." |
||||
label: "Code example to reproduce the issue:" |
||||
placeholder: "`contract A {}`\n" |
||||
id: reproduce |
||||
type: textarea |
||||
validations: |
||||
required: true |
||||
- type: textarea |
||||
id: version |
||||
- |
||||
attributes: |
||||
label: Version |
||||
description: What version of our software are you running? |
||||
value: Run `slither --version` |
||||
description: | |
||||
What version of slither are you running? |
||||
Run `slither --version` |
||||
label: "Version:" |
||||
id: version |
||||
type: textarea |
||||
validations: |
||||
required: true |
||||
- type: textarea |
||||
id: logs |
||||
- |
||||
attributes: |
||||
label: Relevant log output |
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. |
||||
description: | |
||||
Please copy and paste any relevant log output. This |
||||
will be automatically formatted into code, so no need for backticks. |
||||
render: shell |
||||
label: "Relevant log output:" |
||||
id: logs |
||||
type: textarea |
||||
description: "File a bug report" |
||||
labels: |
||||
- bug-candidate |
||||
name: "Bug Report" |
||||
title: "[Bug-Candidate]: " |
||||
|
@ -0,0 +1,42 @@ |
||||
--- |
||||
name: IR tests |
||||
|
||||
defaults: |
||||
run: |
||||
# To load bashrc |
||||
shell: bash -ieo pipefail {0} |
||||
|
||||
on: |
||||
pull_request: |
||||
branches: [master, dev] |
||||
schedule: |
||||
# run CI every day even if no PRs/merges occur |
||||
- cron: '0 12 * * *' |
||||
|
||||
jobs: |
||||
build: |
||||
name: IR tests |
||||
runs-on: ${{ matrix.os }} |
||||
strategy: |
||||
fail-fast: false |
||||
matrix: |
||||
os: [ubuntu-latest, windows-2022] |
||||
|
||||
steps: |
||||
- name: Checkout Code |
||||
uses: actions/checkout@v2 |
||||
|
||||
- name: Set up Python 3.8 |
||||
uses: actions/setup-python@v3 |
||||
with: |
||||
python-version: 3.8 |
||||
|
||||
- name: Install dependencies |
||||
run: | |
||||
pip install ".[dev]" |
||||
solc-select install all |
||||
solc-select use 0.8.11 |
||||
|
||||
- name: Test with pytest |
||||
run: | |
||||
pytest tests/test_ssa_generation.py |
@ -0,0 +1,50 @@ |
||||
--- |
||||
name: Test slither-read-storage |
||||
|
||||
defaults: |
||||
run: |
||||
# To load bashrc |
||||
shell: bash -ieo pipefail {0} |
||||
|
||||
on: |
||||
pull_request: |
||||
branches: [master, dev] |
||||
schedule: |
||||
# run CI every day even if no PRs/merges occur |
||||
- cron: '0 12 * * *' |
||||
|
||||
jobs: |
||||
build: |
||||
name: Test slither-read-storage |
||||
runs-on: ubuntu-latest |
||||
|
||||
steps: |
||||
- uses: actions/checkout@v2 |
||||
- name: Setup node |
||||
uses: actions/setup-node@v2 |
||||
with: |
||||
node-version: '14' |
||||
|
||||
- name: Install ganache |
||||
run: npm install --global ganache |
||||
|
||||
- name: Set up Python 3.8 |
||||
uses: actions/setup-python@v2 |
||||
with: |
||||
python-version: 3.8 |
||||
|
||||
- name: Install python dependencies |
||||
run: | |
||||
pip install ".[dev]" |
||||
pip install web3 |
||||
solc-select install 0.8.1 |
||||
solc-select install 0.8.10 |
||||
solc-select use 0.8.1 |
||||
|
||||
- name: Run slither-read-storage |
||||
run: | |
||||
pytest tests/test_read_storage.py |
||||
|
||||
- name: Run storage layout tests |
||||
run: | |
||||
pytest tests/test_storage_layout.py |
@ -0,0 +1,45 @@ |
||||
from typing import TYPE_CHECKING, Tuple |
||||
|
||||
from slither.core.children.child_contract import ChildContract |
||||
from slither.core.declarations.top_level import TopLevel |
||||
from slither.core.solidity_types import Type |
||||
|
||||
if TYPE_CHECKING: |
||||
from slither.core.declarations import Contract |
||||
from slither.core.scope.scope import FileScope |
||||
|
||||
|
||||
class TypeAlias(Type): |
||||
def __init__(self, underlying_type: Type, name: str): |
||||
super().__init__() |
||||
self.name = name |
||||
self.underlying_type = underlying_type |
||||
|
||||
@property |
||||
def storage_size(self) -> Tuple[int, bool]: |
||||
return self.underlying_type.storage_size |
||||
|
||||
def __hash__(self): |
||||
return hash(str(self)) |
||||
|
||||
@property |
||||
def is_dynamic(self) -> bool: |
||||
return self.underlying_type.is_dynamic |
||||
|
||||
|
||||
class TypeAliasTopLevel(TypeAlias, TopLevel): |
||||
def __init__(self, underlying_type: Type, name: str, scope: "FileScope"): |
||||
super().__init__(underlying_type, name) |
||||
self.file_scope: "FileScope" = scope |
||||
|
||||
def __str__(self): |
||||
return self.name |
||||
|
||||
|
||||
class TypeAliasContract(TypeAlias, ChildContract): |
||||
def __init__(self, underlying_type: Type, name: str, contract: "Contract"): |
||||
super().__init__(underlying_type, name) |
||||
self._contract: "Contract" = contract |
||||
|
||||
def __str__(self): |
||||
return self.contract.name + "." + self.name |
@ -0,0 +1,95 @@ |
||||
from typing import List |
||||
from slither.core.cfg.node import Node |
||||
from slither.core.declarations.solidity_variables import SolidityVariable |
||||
from slither.slithir.operations import HighLevelCall, LibraryCall |
||||
from slither.core.declarations import Contract, Function, SolidityVariableComposed |
||||
from slither.analyses.data_dependency.data_dependency import is_dependent |
||||
from slither.core.compilation_unit import SlitherCompilationUnit |
||||
|
||||
|
||||
class ArbitrarySendErc20: |
||||
"""Detects instances where ERC20 can be sent from an arbitrary from address.""" |
||||
|
||||
def __init__(self, compilation_unit: SlitherCompilationUnit): |
||||
self._compilation_unit = compilation_unit |
||||
self._no_permit_results: List[Node] = [] |
||||
self._permit_results: List[Node] = [] |
||||
|
||||
@property |
||||
def compilation_unit(self) -> SlitherCompilationUnit: |
||||
return self._compilation_unit |
||||
|
||||
@property |
||||
def no_permit_results(self) -> List[Node]: |
||||
return self._no_permit_results |
||||
|
||||
@property |
||||
def permit_results(self) -> List[Node]: |
||||
return self._permit_results |
||||
|
||||
def _detect_arbitrary_from(self, contract: Contract): |
||||
for f in contract.functions: |
||||
all_high_level_calls = [ |
||||
f_called[1].solidity_signature |
||||
for f_called in f.high_level_calls |
||||
if isinstance(f_called[1], Function) |
||||
] |
||||
all_library_calls = [f_called[1].solidity_signature for f_called in f.library_calls] |
||||
if ( |
||||
"transferFrom(address,address,uint256)" in all_high_level_calls |
||||
or "safeTransferFrom(address,address,address,uint256)" in all_library_calls |
||||
): |
||||
if ( |
||||
"permit(address,address,uint256,uint256,uint8,bytes32,bytes32)" |
||||
in all_high_level_calls |
||||
): |
||||
ArbitrarySendErc20._arbitrary_from(f.nodes, self._permit_results) |
||||
else: |
||||
ArbitrarySendErc20._arbitrary_from(f.nodes, self._no_permit_results) |
||||
|
||||
@staticmethod |
||||
def _arbitrary_from(nodes: List[Node], results: List[Node]): |
||||
"""Finds instances of (safe)transferFrom that do not use msg.sender or address(this) as from parameter.""" |
||||
for node in nodes: |
||||
for ir in node.irs: |
||||
if ( |
||||
isinstance(ir, HighLevelCall) |
||||
and isinstance(ir.function, Function) |
||||
and ir.function.solidity_signature == "transferFrom(address,address,uint256)" |
||||
and not ( |
||||
is_dependent( |
||||
ir.arguments[0], |
||||
SolidityVariableComposed("msg.sender"), |
||||
node.function.contract, |
||||
) |
||||
or is_dependent( |
||||
ir.arguments[0], |
||||
SolidityVariable("this"), |
||||
node.function.contract, |
||||
) |
||||
) |
||||
): |
||||
results.append(ir.node) |
||||
elif ( |
||||
isinstance(ir, LibraryCall) |
||||
and ir.function.solidity_signature |
||||
== "safeTransferFrom(address,address,address,uint256)" |
||||
and not ( |
||||
is_dependent( |
||||
ir.arguments[1], |
||||
SolidityVariableComposed("msg.sender"), |
||||
node.function.contract, |
||||
) |
||||
or is_dependent( |
||||
ir.arguments[1], |
||||
SolidityVariable("this"), |
||||
node.function.contract, |
||||
) |
||||
) |
||||
): |
||||
results.append(ir.node) |
||||
|
||||
def detect(self): |
||||
"""Detect transfers that use arbitrary `from` parameter.""" |
||||
for c in self.compilation_unit.contracts_derived: |
||||
self._detect_arbitrary_from(c) |
@ -0,0 +1,45 @@ |
||||
from typing import List |
||||
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification |
||||
from slither.utils.output import Output |
||||
from .arbitrary_send_erc20 import ArbitrarySendErc20 |
||||
|
||||
|
||||
class ArbitrarySendErc20NoPermit(AbstractDetector): |
||||
""" |
||||
Detect when `msg.sender` is not used as `from` in transferFrom |
||||
""" |
||||
|
||||
ARGUMENT = "arbitrary-send-erc20" |
||||
HELP = "transferFrom uses arbitrary `from`" |
||||
IMPACT = DetectorClassification.HIGH |
||||
CONFIDENCE = DetectorClassification.HIGH |
||||
|
||||
WIKI = "https://github.com/trailofbits/slither/wiki/Detector-Documentation#arbitrary-send-erc20" |
||||
|
||||
WIKI_TITLE = "Arbitrary `from` in transferFrom" |
||||
WIKI_DESCRIPTION = "Detect when `msg.sender` is not used as `from` in transferFrom." |
||||
WIKI_EXPLOIT_SCENARIO = """ |
||||
```solidity |
||||
function a(address from, address to, uint256 amount) public { |
||||
erc20.transferFrom(from, to, am); |
||||
} |
||||
``` |
||||
Alice approves this contract to spend her ERC20 tokens. Bob can call `a` and specify Alice's address as the `from` parameter in `transferFrom`, allowing him to transfer Alice's tokens to himself.""" |
||||
|
||||
WIKI_RECOMMENDATION = """ |
||||
Use `msg.sender` as `from` in transferFrom. |
||||
""" |
||||
|
||||
def _detect(self) -> List[Output]: |
||||
"""""" |
||||
results: List[Output] = [] |
||||
|
||||
arbitrary_sends = ArbitrarySendErc20(self.compilation_unit) |
||||
arbitrary_sends.detect() |
||||
for node in arbitrary_sends.no_permit_results: |
||||
func = node.function |
||||
info = [func, " uses arbitrary from in transferFrom: ", node, "\n"] |
||||
res = self.generate_result(info) |
||||
results.append(res) |
||||
|
||||
return results |
@ -0,0 +1,53 @@ |
||||
from typing import List |
||||
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification |
||||
from slither.utils.output import Output |
||||
from .arbitrary_send_erc20 import ArbitrarySendErc20 |
||||
|
||||
|
||||
class ArbitrarySendErc20Permit(AbstractDetector): |
||||
""" |
||||
Detect when `msg.sender` is not used as `from` in transferFrom along with the use of permit. |
||||
""" |
||||
|
||||
ARGUMENT = "arbitrary-send-erc20-permit" |
||||
HELP = "transferFrom uses arbitrary from with permit" |
||||
IMPACT = DetectorClassification.HIGH |
||||
CONFIDENCE = DetectorClassification.MEDIUM |
||||
|
||||
WIKI = "https://github.com/trailofbits/slither/wiki/Detector-Documentation#arbitrary-send-erc20-permit" |
||||
|
||||
WIKI_TITLE = "Arbitrary `from` in transferFrom used with permit" |
||||
WIKI_DESCRIPTION = ( |
||||
"Detect when `msg.sender` is not used as `from` in transferFrom and permit is used." |
||||
) |
||||
WIKI_EXPLOIT_SCENARIO = """ |
||||
```solidity |
||||
function bad(address from, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s, address to) public { |
||||
erc20.permit(from, address(this), value, deadline, v, r, s); |
||||
erc20.transferFrom(from, to, value); |
||||
} |
||||
``` |
||||
If an ERC20 token does not implement permit and has a fallback function e.g. WETH, transferFrom allows an attacker to transfer all tokens approved for this contract.""" |
||||
|
||||
WIKI_RECOMMENDATION = """ |
||||
Ensure that the underlying ERC20 token correctly implements a permit function. |
||||
""" |
||||
|
||||
def _detect(self) -> List[Output]: |
||||
"""""" |
||||
results: List[Output] = [] |
||||
|
||||
arbitrary_sends = ArbitrarySendErc20(self.compilation_unit) |
||||
arbitrary_sends.detect() |
||||
for node in arbitrary_sends.permit_results: |
||||
func = node.function |
||||
info = [ |
||||
func, |
||||
" uses arbitrary from in transferFrom in combination with permit: ", |
||||
node, |
||||
"\n", |
||||
] |
||||
res = self.generate_result(info) |
||||
results.append(res) |
||||
|
||||
return results |
@ -0,0 +1,81 @@ |
||||
""" |
||||
Module detecting suicidal contract |
||||
|
||||
A suicidal contract is an unprotected function that calls selfdestruct |
||||
""" |
||||
from typing import List |
||||
|
||||
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification |
||||
from slither.core.declarations import Function, Contract |
||||
from slither.utils.output import Output |
||||
|
||||
|
||||
class ProtectedVariables(AbstractDetector): |
||||
|
||||
ARGUMENT = "protected-vars" |
||||
HELP = "Detected unprotected variables" |
||||
IMPACT = DetectorClassification.HIGH |
||||
CONFIDENCE = DetectorClassification.HIGH |
||||
|
||||
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#protected-variables" |
||||
|
||||
WIKI_TITLE = "Protected Variables" |
||||
WIKI_DESCRIPTION = "Detect unprotected variable that are marked protected" |
||||
|
||||
# region wiki_exploit_scenario |
||||
WIKI_EXPLOIT_SCENARIO = """ |
||||
```solidity |
||||
contract Buggy{ |
||||
|
||||
/// @custom:security write-protection="onlyOwner()" |
||||
address owner; |
||||
|
||||
function set_protected() public onlyOwner(){ |
||||
owner = msg.sender; |
||||
} |
||||
|
||||
function set_not_protected() public{ |
||||
owner = msg.sender; |
||||
} |
||||
} |
||||
``` |
||||
`owner` must be always written by function using `onlyOwner` (`write-protection="onlyOwner()"`), however anyone can call `set_not_protected`. |
||||
""" |
||||
# endregion wiki_exploit_scenario |
||||
|
||||
WIKI_RECOMMENDATION = "Add access controls to the vulnerable function" |
||||
|
||||
def _analyze_function(self, function: Function, contract: Contract) -> List[Output]: |
||||
results = [] |
||||
|
||||
for state_variable_written in function.state_variables_written: |
||||
if state_variable_written.write_protection: |
||||
for function_sig in state_variable_written.write_protection: |
||||
function_protection = contract.get_function_from_signature(function_sig) |
||||
if not function_protection: |
||||
function_protection = contract.get_modifier_from_signature(function_sig) |
||||
if not function_protection: |
||||
self.logger.error(f"{function_sig} not found") |
||||
continue |
||||
if function_protection not in function.all_internal_calls(): |
||||
info = [ |
||||
function, |
||||
" should have ", |
||||
function_protection, |
||||
" to protect ", |
||||
state_variable_written, |
||||
"\n", |
||||
] |
||||
|
||||
res = self.generate_result(info) |
||||
results.append(res) |
||||
return results |
||||
|
||||
def _detect(self): |
||||
"""Detect the suicidal functions""" |
||||
results = [] |
||||
for contract in self.compilation_unit.contracts_derived: |
||||
for function in contract.functions_entry_points: |
||||
results += self._analyze_function(function, contract) |
||||
|
||||
return results |
@ -0,0 +1,30 @@ |
||||
from slither.core.declarations import Contract |
||||
from slither.core.variables.state_variable import StateVariable |
||||
from slither.core.solidity_types import ArrayType, ElementaryType |
||||
|
||||
|
||||
def is_upgradable_gap_variable(contract: Contract, variable: StateVariable) -> bool: |
||||
"""Helper function that returns true if 'variable' is a gap variable used |
||||
for upgradable contracts. More specifically, the function returns true if: |
||||
- variable is named "__gap" |
||||
- it is a uint256 array declared at the end of the contract |
||||
- it has private visibility""" |
||||
|
||||
# Return early on if the variable name is != gap to avoid iterating over all the state variables |
||||
if variable.name != "__gap": |
||||
return False |
||||
|
||||
declared_variable_ordered = [ |
||||
v for v in contract.state_variables_ordered if v in contract.state_variables_declared |
||||
] |
||||
|
||||
if not declared_variable_ordered: |
||||
return False |
||||
|
||||
variable_type = variable.type |
||||
return ( |
||||
declared_variable_ordered[-1] is variable |
||||
and isinstance(variable_type, ArrayType) |
||||
and variable_type.type == ElementaryType("uint256") |
||||
and variable.visibility == "private" |
||||
) |
@ -0,0 +1,61 @@ |
||||
""" |
||||
Module printing summary of the contract |
||||
""" |
||||
|
||||
from slither.core.declarations import Function |
||||
from slither.core.declarations.function import SolidityFunction |
||||
from slither.printers.abstract_printer import AbstractPrinter |
||||
from slither.utils import output |
||||
from slither.utils.myprettytable import MyPrettyTable |
||||
|
||||
|
||||
def _use_modifier(function: Function, modifier_name: str = "whenNotPaused") -> bool: |
||||
if function.is_constructor or function.view or function.pure: |
||||
return False |
||||
|
||||
for internal_call in function.all_internal_calls(): |
||||
if isinstance(internal_call, SolidityFunction): |
||||
continue |
||||
if any(modifier.name == modifier_name for modifier in function.modifiers): |
||||
return True |
||||
return False |
||||
|
||||
|
||||
class PrinterWhenNotPaused(AbstractPrinter): |
||||
|
||||
ARGUMENT = "pausable" |
||||
HELP = "Print functions that do not use whenNotPaused" |
||||
|
||||
WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#when-not-paused" |
||||
|
||||
def output(self, _filename: str) -> output.Output: |
||||
""" |
||||
_filename is not used |
||||
Args: |
||||
_filename(string) |
||||
""" |
||||
|
||||
modifier_name: str = "whenNotPaused" |
||||
|
||||
txt = "" |
||||
txt += "Constructor and pure/view functions are not displayed\n" |
||||
all_tables = [] |
||||
for contract in self.slither.contracts: |
||||
|
||||
txt += f"\n{contract.name}:\n" |
||||
table = MyPrettyTable(["Name", "Use whenNotPaused"]) |
||||
|
||||
for function in contract.functions_entry_points: |
||||
status = "X" if _use_modifier(function, modifier_name) else "" |
||||
table.add_row([function.solidity_signature, status]) |
||||
|
||||
txt += str(table) + "\n" |
||||
all_tables.append((contract.name, table)) |
||||
|
||||
self.info(txt) |
||||
|
||||
res = self.generate_output(txt) |
||||
for name, table in all_tables: |
||||
res.add_pretty_table(table, name) |
||||
|
||||
return res |
@ -0,0 +1,91 @@ |
||||
# Slither-read-storage |
||||
|
||||
Slither-read-storage is a tool to retrieve the storage slots and values of entire contracts or single variables. |
||||
|
||||
## Usage |
||||
|
||||
### CLI Interface |
||||
|
||||
```shell |
||||
positional arguments: |
||||
contract_source (DIR) ADDRESS The deployed contract address if verified on etherscan. Prepend project directory for unverified contracts. |
||||
|
||||
optional arguments: |
||||
--variable-name VARIABLE_NAME The name of the variable whose value will be returned. |
||||
--rpc-url RPC_URL An endpoint for web3 requests. |
||||
--key KEY The key/ index whose value will be returned from a mapping or array. |
||||
--deep-key DEEP_KEY The key/ index whose value will be returned from a deep mapping or multidimensional array. |
||||
--struct-var STRUCT_VAR The name of the variable whose value will be returned from a struct. |
||||
--storage-address STORAGE_ADDRESS The address of the storage contract (if a proxy pattern is used). |
||||
--contract-name CONTRACT_NAME The name of the logic contract. |
||||
--layout Toggle used to write a JSON file with the entire storage layout. |
||||
--value Toggle used to include values in output. |
||||
--max-depth MAX_DEPTH Max depth to search in data structure. |
||||
``` |
||||
|
||||
### Examples |
||||
|
||||
Retrieve the storage slots of a local contract: |
||||
|
||||
```shell |
||||
slither-read-storage file.sol 0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8 --layout |
||||
``` |
||||
|
||||
Retrieve the storage slots of a contract verified on an Etherscan-like platform: |
||||
|
||||
```shell |
||||
slither-read-storage 0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8 --layout |
||||
``` |
||||
|
||||
To retrieve the values as well, pass `--value` and `--rpc-url $RPC_URL`: |
||||
|
||||
```shell |
||||
slither-read-storage 0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8 --layout --rpc-url $RPC_URL --value |
||||
``` |
||||
|
||||
To view only the slot of the `slot0` structure variable, pass `--variable-name slot0`: |
||||
|
||||
```shell |
||||
slither-read-storage 0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8 --variable-name slot0 --rpc-url $RPC_URL --value |
||||
``` |
||||
|
||||
To view a member of the `slot0` struct, pass `--struct-var tick` |
||||
|
||||
```shell |
||||
slither-read-storage 0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8 --variable-name slot0 --rpc-url $RPC_URL --value --struct-var tick |
||||
``` |
||||
|
||||
Retrieve the ERC20 balance slot of an account: |
||||
|
||||
```shell |
||||
slither-read-storage 0xa2327a938Febf5FEC13baCFb16Ae10EcBc4cbDCF --variable-name balances --key 0xab5801a7d398351b8be11c439e05c5b3259aec9b |
||||
``` |
||||
|
||||
To retrieve the actual balance, pass `--variable-name balances` and `--key 0xab5801a7d398351b8be11c439e05c5b3259aec9b`. (`balances` is a `mapping(address => uint)`) |
||||
Since this contract uses the delegatecall-proxy pattern, the proxy address must be passed as the `--storage-address`. Otherwise, it is not required. |
||||
|
||||
```shell |
||||
slither-read-storage 0xa2327a938Febf5FEC13baCFb16Ae10EcBc4cbDCF --variable-name balances --key 0xab5801a7d398351b8be11c439e05c5b3259aec9b --storage-address 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 --rpc-url $RPC_URL --value |
||||
``` |
||||
|
||||
## Troubleshooting/FAQ |
||||
|
||||
- If the storage slots or values of a contract verified Etherscan are wrong, try passing `--contract $CONTRACT_NAME` explicitly. Otherwise, the storage may be retrieved from storage slots based off an unrelated contract (Etherscan includes these). (Also, make sure that the RPC is for the correct network.) |
||||
|
||||
- If Etherscan fails to return a source code, try passing `--etherscan-apikey $API_KEY` to avoid hitting a rate-limit. |
||||
|
||||
- How do I use this tool on other chains? |
||||
If an EVM chain has an Etherscan-like platform the crytic-compile supports, this tool supports it by making the following modifications. |
||||
Take Avalanche, for instance: |
||||
|
||||
```shell |
||||
slither-read-storage avax:0x0000000000000000000000000000000000000000 --layout --value --rpc-url $AVAX_RPC_URL |
||||
``` |
||||
|
||||
## Limitations |
||||
|
||||
- Requires source code. |
||||
- Only works on Solidity contracts. |
||||
- Cannot find variables with unstructured storage. |
||||
- Does not support all data types (please open an issue or PR). |
||||
- Mappings cannot be completely enumerated since all keys used historically are not immediately available. |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue