Repository: crytic/slither

Issue:
`slither-flat` doesn't flatten dYdX solo contracts correctly
Hi There,

Thanks for building `slither-flat`! It seems to be a lot more forgivign than `soljitsu` or `truffle-flatten`. I'm struggling, however, with using it to flatten dYdX contracts.

**How to reproduce**

```bash
#!/usr/bin/bash

# Dependency: npm, nvm
# Better not to run this as a script

# Get the repository
echo "solo" > .gitignore
git clone https://github.com/dydxprotocol/solo.git
cd solo

# Install javascript dependencies
npm i keccakjs --save
npm i solium@^1.0.0 --save
npm i acorn --save
npm i

# Testing that everything installed correctly (make sure Docker is enabled at this point)
nvm use 10.16.2  # Need this version to compile contracts
npm test

# Install and run slither-flat
cd ..
pipenv install slither-analyzer
pipenv run slither-flat solo
```

**Issues**

I noticed two issues with these contracts:

1) Duplicate contracts

In particular, `ReentrancyGuard` appeared many times in different contract files (and multiple times per file)

2) Missing characters

In particular, `ReentrancyGuard` appeared as `ontract ReentrancyGuard` (missing a `c`)

3) Some contracts don't exist in their own file anymore

Contract `external/OnlySolo.sol` doesn't have a corresponding `OnlySolo.sol` file in the output, instead it's embedded inline with other contracts in multiple places. [perhaps this is intentional]

**Potential problems**

With `truffle-flatten` it flagged a circular dependency in the contracts, so perhaps this is related to the issue here.
`slither-flat` doesn't flatten dYdX solo contracts correctly
Hi There,

Thanks for building `slither-flat`! It seems to be a lot more forgivign than `soljitsu` or `truffle-flatten`. I'm struggling, however, with using it to flatten dYdX contracts.

**How to reproduce**

```bash
#!/usr/bin/bash

# Dependency: npm, nvm
# Better not to run this as a script

# Get the repository
echo "solo" > .gitignore
git clone https://github.com/dydxprotocol/solo.git
cd solo

# Install javascript dependencies
npm i keccakjs --save
npm i solium@^1.0.0 --save
npm i acorn --save
npm i

# Testing that everything installed correctly (make sure Docker is enabled at this point)
nvm use 10.16.2  # Need this version to compile contracts
npm test

# Install and run slither-flat
cd ..
pipenv install slither-analyzer
pipenv run slither-flat solo
```

**Issues**

I noticed two issues with these contracts:

1) Duplicate contracts

In particular, `ReentrancyGuard` appeared many times in different contract files (and multiple times per file)

2) Missing characters

In particular, `ReentrancyGuard` appeared as `ontract ReentrancyGuard` (missing a `c`)

3) Some contracts don't exist in their own file anymore

Contract `external/OnlySolo.sol` doesn't have a corresponding `OnlySolo.sol` file in the output, instead it's embedded inline with other contracts in multiple places. [perhaps this is intentional]

**Potential problems**

With `truffle-flatten` it flagged a circular dependency in the contracts, so perhaps this is related to the issue here.


Discussion:
Hi @Pet3ris, thank you for reporting this.

We will fix it as soon as possible
Hi @Pet3ris, thank you for reporting this.

We will fix it as soon as possible

Resolving patch:
diff --git a/slither/tools/flattening/flattening.py b/slither/tools/flattening/flattening.py
--- a/slither/tools/flattening/flattening.py
+++ b/slither/tools/flattening/flattening.py
@@ -4,6 +4,7 @@
 from collections import namedtuple
 
 from slither.core.declarations import SolidityFunction
+from slither.core.solidity_types import MappingType, ArrayType
 from slither.exceptions import SlitherException
 from slither.core.solidity_types.user_defined_type import UserDefinedType
 from slither.core.declarations.structure import Structure
@@ -20,6 +21,7 @@
 #   - line_removal: remove the line (remove-assert)
 Patch = namedtuple('PatchExternal', ['index', 'patch_type'])
 
+
 class Flattening:
     DEFAULT_EXPORT_PATH = Path('crytic-export/flattening')
 
@@ -44,7 +46,7 @@ def _check_abi_encoder_v2(self):
 
     def _get_source_code(self, contract):
         src_mapping = contract.source_mapping
-        content = self._slither.source_code[src_mapping['filename_absolute']]
+        content = self._slither.source_code[src_mapping['filename_absolute']].encode('utf8')
         start = src_mapping['start']
         end = src_mapping['start'] + src_mapping['length']
 
@@ -112,17 +114,22 @@ def _get_source_code(self, contract):
                 assert patch_type == 'line_removal'
                 content = content[:index] + ' // ' + content[index:]
 
-        self._source_codes[contract] = content
+        self._source_codes[contract] = content.decode('utf8')
 
     def _export_from_type(self, t, contract, exported, list_contract):
         if isinstance(t, UserDefinedType):
             if isinstance(t.type, (Enum, Structure)):
-                if t.type.contract != contract and not t.type.contract in exported:
+                if t.type.contract != contract and t.type.contract not in exported:
                     self._export_contract(t.type.contract, exported, list_contract)
             else:
                 assert isinstance(t.type, Contract)
-                if t.type != contract and not t.type in exported:
+                if t.type != contract and t.type not in exported:
                     self._export_contract(t.type, exported, list_contract)
+        elif isinstance(t, MappingType):
+            self._export_from_type(t.type_from, contract, exported, list_contract)
+            self._export_from_type(t.type_to, contract, exported, list_contract)
+        elif isinstance(t, ArrayType):
+            self._export_from_type(t.type, contract, exported, list_contract)
 
     def _export_contract(self, contract, exported, list_contract):
         if contract.name in exported:
@@ -148,6 +155,10 @@ def _export_contract(self, contract, exported, list_contract):
         for v in contract.variables + local_vars:
             self._export_from_type(v.type, contract, exported, list_contract)
 
+        for s in contract.structures:
+            for elem in s.elems.values():
+                self._export_from_type(elem.type, contract, exported, list_contract)
+
         # Find all convert and "new" operation that can lead to use an external contract
         for f in contract.functions_declared:
             for ir in f.slithir_operations:


Test patch: