Repository: crytic/slither

Issue:
Wrong tuple assignment
```
pragma solidity 0.5.0;


contract TuplePoc {

    mapping(address => uint256) public lastWithdrawTime;
    mapping(address => uint256) public balances;


    function withdrawFunds (uint amount) public {
        (bool success, bytes memory data) = msg.sender.call.value(amount)("");
        require(success);
        balances[msg.sender] -= amount;
        lastWithdrawTime[msg.sender] = now;
    }
 }

```
The generated IR for the above smart contract follows:

```
TUPLE_0(bool) = LOW_LEVEL_CALL, dest:msg.sender, function:call, arguments:[''] value:amount 
success = UNPACK TUPLE_0 index: 0 
data = UNPACK TUPLE_0 index: 1 
```
The above generated IR wrongly generates tuple assignments. The returned value for the low-level call in the source code contains a tuple of two variables. But, but the generated IR contains only one variable. 

Wrong tuple assignment
```
pragma solidity 0.5.0;


contract TuplePoc {

    mapping(address => uint256) public lastWithdrawTime;
    mapping(address => uint256) public balances;


    function withdrawFunds (uint amount) public {
        (bool success, bytes memory data) = msg.sender.call.value(amount)("");
        require(success);
        balances[msg.sender] -= amount;
        lastWithdrawTime[msg.sender] = now;
    }
 }

```
The generated IR for the above smart contract follows:

```
TUPLE_0(bool) = LOW_LEVEL_CALL, dest:msg.sender, function:call, arguments:[''] value:amount 
success = UNPACK TUPLE_0 index: 0 
data = UNPACK TUPLE_0 index: 1 
```
The above generated IR wrongly generates tuple assignments. The returned value for the low-level call in the source code contains a tuple of two variables. But, but the generated IR contains only one variable. 



Discussion:
Hi @PriyankaBose, the fix (#536) is merged in master. It will also be available in the upcoming release.

Thank you for reporting this issue. 

#536 is actually incorrect; I am reopening this issue and I am working on a better fix
Hi @PriyankaBose, the fix (#536) is merged in master. It will also be available in the upcoming release.

Thank you for reporting this issue. 

#536 is actually incorrect; I am reopening this issue and I am working on a better fix

Resolving patch:
diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py
--- a/slither/slithir/convert.py
+++ b/slither/slithir/convert.py
@@ -763,7 +763,10 @@ def convert_to_low_level(ir):
         new_ir.call_gas = ir.call_gas
         new_ir.call_value = ir.call_value
         new_ir.arguments = ir.arguments
-        new_ir.lvalue.set_type(ElementaryType('bool'))
+        if ir.slither.solc_version >= "0.5":
+            new_ir.lvalue.set_type([ElementaryType('bool'), ElementaryType('bytes')])
+        else:
+            new_ir.lvalue.set_type(ElementaryType('bool'))
         new_ir.set_expression(ir.expression)
         new_ir.set_node(ir.node)
         return new_ir
diff --git a/slither/slithir/operations/low_level_call.py b/slither/slithir/operations/low_level_call.py
--- a/slither/slithir/operations/low_level_call.py
+++ b/slither/slithir/operations/low_level_call.py
@@ -95,9 +95,14 @@ def __str__(self):
         arguments = []
         if self.arguments:
             arguments = self.arguments
+        return_type = self.lvalue.type
+
+        if return_type and isinstance(return_type, list):
+            return_type = ','.join(str(x) for x in return_type)
+
         txt = '{}({}) = LOW_LEVEL_CALL, dest:{}, function:{}, arguments:{} {} {}'
         return txt.format(self.lvalue,
-                          self.lvalue.type,
+                          return_type,
                           self.destination,
                           self.function_name,
                           [str(x) for x in arguments],
diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py
--- a/slither/visitors/slithir/expression_to_slithir.py
+++ b/slither/visitors/slithir/expression_to_slithir.py
@@ -115,14 +115,11 @@ def _post_assignement_operation(self, expression):
                 set_val(expression, None)
             else:
                 assert isinstance(right, TupleVariable)
-                tuple_types = []
                 for idx in range(len(left)):
                     if not left[idx] is None:
                         operation = Unpack(left[idx], right, idx)
                         operation.set_expression(expression)
-                        tuple_types.append(left[idx].type)
                         self._result.append(operation)
-                right.set_type(tuple_types)
                 set_val(expression, None)
         else:
             # Init of array, like


Test patch: