Skip to content

Commit

Permalink
Merge pull request #1898 from 0xGusMcCrae/slither-interface
Browse files Browse the repository at this point in the history
add tool slither-interface
  • Loading branch information
montyly authored May 16, 2023
2 parents 5917de9 + 6fbb366 commit 9185f54
Show file tree
Hide file tree
Showing 13 changed files with 358 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
"etherscan",
"find_paths",
"flat",
"interface",
"kspec",
"printers",
# "prop"
Expand Down
95 changes: 95 additions & 0 deletions scripts/ci_test_interface.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/env bash

### Test slither-interface

DIR_TESTS="tests/tools/interface"

solc-select use 0.8.19 --always-install

#Test 1 - Etherscan target
slither-interface WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
DIFF=$(diff crytic-export/interfaces/IWETH9.sol "$DIR_TESTS/test_1.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 1 failed"
cat "crytic-export/interfaces/IWETH9.sol"
echo ""
cat "$DIR_TESTS/test_1.sol"
exit 255
fi


#Test 2 - Local file target
slither-interface Mock tests/tools/interface/ContractMock.sol
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_2.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 2 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_2.sol"
exit 255
fi


#Test 3 - unroll structs
slither-interface Mock tests/tools/interface/ContractMock.sol --unroll-structs
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_3.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 3 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_3.sol"
exit 255
fi

#Test 4 - exclude structs
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-structs
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_4.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 4 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_4.sol"
exit 255
fi

#Test 5 - exclude errors
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-errors
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_5.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 5 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_5.sol"
exit 255
fi

#Test 6 - exclude enums
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-enums
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_6.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 6 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_6.sol"
exit 255
fi

#Test 7 - exclude events
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-events
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_7.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 7 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_7.sol"
exit 255
fi

rm -r crytic-export
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"slither-read-storage = slither.tools.read_storage.__main__:main",
"slither-doctor = slither.tools.doctor.__main__:main",
"slither-documentation = slither.tools.documentation.__main__:main",
"slither-interface = slither.tools.interface.__main__:main",
]
},
)
Empty file.
105 changes: 105 additions & 0 deletions slither/tools/interface/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import argparse
import logging
from pathlib import Path

from crytic_compile import cryticparser

from slither import Slither
from slither.utils.code_generation import generate_interface

logging.basicConfig()
logger = logging.getLogger("Slither-Interface")
logger.setLevel(logging.INFO)


def parse_args() -> argparse.Namespace:
"""
Parse the underlying arguments for the program.
:return: Returns the arguments for the program.
"""
parser = argparse.ArgumentParser(
description="Generates code for a Solidity interface from contract",
usage=("slither-interface <ContractName> <source file or deployment address>"),
)

parser.add_argument(
"contract_source",
help="The name of the contract (case sensitive) followed by the deployed contract address if verified on etherscan or project directory/filename for local contracts.",
nargs="+",
)

parser.add_argument(
"--unroll-structs",
help="Whether to use structures' underlying types instead of the user-defined type",
default=False,
action="store_true",
)

parser.add_argument(
"--exclude-events",
help="Excludes event signatures in the interface",
default=False,
action="store_true",
)

parser.add_argument(
"--exclude-errors",
help="Excludes custom error signatures in the interface",
default=False,
action="store_true",
)

parser.add_argument(
"--exclude-enums",
help="Excludes enum definitions in the interface",
default=False,
action="store_true",
)

parser.add_argument(
"--exclude-structs",
help="Exclude struct definitions in the interface",
default=False,
action="store_true",
)

cryticparser.init(parser)

return parser.parse_args()


def main() -> None:
args = parse_args()

contract_name, target = args.contract_source
slither = Slither(target, **vars(args))

_contract = slither.get_contract_from_name(contract_name)[0]

interface = generate_interface(
contract=_contract,
unroll_structs=args.unroll_structs,
include_events=not args.exclude_events,
include_errors=not args.exclude_errors,
include_enums=not args.exclude_enums,
include_structs=not args.exclude_structs,
)

# add version pragma
interface = (
f"pragma solidity {_contract.compilation_unit.pragma_directives[0].version};\n\n"
+ interface
)

# write interface to file
export = Path("crytic-export", "interfaces")
export.mkdir(parents=True, exist_ok=True)
filename = f"I{contract_name}.sol"
path = Path(export, filename)
logger.info(f" Interface exported to {path}")
with open(path, "w", encoding="utf8") as f:
f.write(interface)


if __name__ == "__main__":
main()
33 changes: 33 additions & 0 deletions tests/tools/interface/ContractMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
pragma solidity ^0.8.19;

contract Mock {

error Error1();
error Error2();
error Error3();

event Event1();
event Event2(address param);
event Event3(uint256 num1, uint72 num2);

struct Foo {
uint256 bar;
address baz;
}

enum Status {
Active,
Pending,
Canceled
}

Foo public foo;

Status public status;

function function1() public pure returns (address){
return address(0);
}


}
20 changes: 20 additions & 0 deletions tests/tools/interface/test_1.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pragma solidity ^0.4.18;

interface IWETH9 {
event Approval(address, address, uint256);
event Transfer(address, address, uint256);
event Deposit(address, uint256);
event Withdrawal(address, uint256);
function name() external returns (string memory);
function symbol() external returns (string memory);
function decimals() external returns (uint8);
function balanceOf(address) external returns (uint256);
function allowance(address,address) external returns (uint256);
function deposit() external payable;
function withdraw(uint256) external;
function totalSupply() external view returns (uint256);
function approve(address,uint256) external returns (bool);
function transfer(address,uint256) external returns (bool);
function transferFrom(address,address,uint256) external returns (bool);
}

19 changes: 19 additions & 0 deletions tests/tools/interface/test_2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pragma solidity ^0.8.19;

interface IMock {
event Event1();
event Event2(address);
event Event3(uint256, uint72);
error Error1();
error Error2();
error Error3();
enum Status { Active, Pending, Canceled }
struct Foo {
uint256 bar;
address baz;
}
function foo() external returns (Foo memory);
function status() external returns (Status);
function function1() external pure returns (address);
}

19 changes: 19 additions & 0 deletions tests/tools/interface/test_3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pragma solidity ^0.8.19;

interface IMock {
event Event1();
event Event2(address);
event Event3(uint256, uint72);
error Error1();
error Error2();
error Error3();
enum Status { Active, Pending, Canceled }
struct Foo {
uint256 bar;
address baz;
}
function foo() external returns (uint256, address);
function status() external returns (uint8);
function function1() external pure returns (address);
}

15 changes: 15 additions & 0 deletions tests/tools/interface/test_4.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity ^0.8.19;

interface IMock {
event Event1();
event Event2(address);
event Event3(uint256, uint72);
error Error1();
error Error2();
error Error3();
enum Status { Active, Pending, Canceled }
function foo() external returns (Foo memory);
function status() external returns (Status);
function function1() external pure returns (address);
}

16 changes: 16 additions & 0 deletions tests/tools/interface/test_5.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pragma solidity ^0.8.19;

interface IMock {
event Event1();
event Event2(address);
event Event3(uint256, uint72);
enum Status { Active, Pending, Canceled }
struct Foo {
uint256 bar;
address baz;
}
function foo() external returns (Foo memory);
function status() external returns (Status);
function function1() external pure returns (address);
}

18 changes: 18 additions & 0 deletions tests/tools/interface/test_6.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pragma solidity ^0.8.19;

interface IMock {
event Event1();
event Event2(address);
event Event3(uint256, uint72);
error Error1();
error Error2();
error Error3();
struct Foo {
uint256 bar;
address baz;
}
function foo() external returns (Foo memory);
function status() external returns (Status);
function function1() external pure returns (address);
}

16 changes: 16 additions & 0 deletions tests/tools/interface/test_7.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pragma solidity ^0.8.19;

interface IMock {
error Error1();
error Error2();
error Error3();
enum Status { Active, Pending, Canceled }
struct Foo {
uint256 bar;
address baz;
}
function foo() external returns (Foo memory);
function status() external returns (Status);
function function1() external pure returns (address);
}

0 comments on commit 9185f54

Please sign in to comment.