Merge pull request #1898 from 0xGusMcCrae/slither-interface

add tool slither-interface
pull/1919/head
Feist Josselin 2 years ago committed by GitHub
commit 9185f54663
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .github/workflows/ci.yml
  2. 95
      scripts/ci_test_interface.sh
  3. 1
      setup.py
  4. 0
      slither/tools/interface/__init__.py
  5. 105
      slither/tools/interface/__main__.py
  6. 33
      tests/tools/interface/ContractMock.sol
  7. 20
      tests/tools/interface/test_1.sol
  8. 19
      tests/tools/interface/test_2.sol
  9. 19
      tests/tools/interface/test_3.sol
  10. 15
      tests/tools/interface/test_4.sol
  11. 16
      tests/tools/interface/test_5.sol
  12. 18
      tests/tools/interface/test_6.sol
  13. 16
      tests/tools/interface/test_7.sol

@ -36,6 +36,7 @@ jobs:
"etherscan",
"find_paths",
"flat",
"interface",
"kspec",
"printers",
# "prop"

@ -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

@ -64,6 +64,7 @@ setup(
"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",
]
},
)

@ -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()

@ -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);
}
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}

@ -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);
}
Loading…
Cancel
Save