slither.slithir.convert

   1import logging
   2from pathlib import Path
   3from typing import Any, List, TYPE_CHECKING, Union, Optional
   4
   5# pylint: disable= too-many-lines,import-outside-toplevel,too-many-branches,too-many-statements,too-many-nested-blocks
   6from slither.core.declarations import (
   7    Contract,
   8    Enum,
   9    Event,
  10    Function,
  11    SolidityFunction,
  12    SolidityVariable,
  13    SolidityVariableComposed,
  14    Structure,
  15)
  16from slither.core.declarations.custom_error import CustomError
  17from slither.core.declarations.function_contract import FunctionContract
  18from slither.core.declarations.function_top_level import FunctionTopLevel
  19from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder
  20from slither.core.declarations.solidity_variables import SolidityCustomRevert
  21from slither.core.expressions import Identifier, Literal
  22from slither.core.expressions.expression import Expression
  23from slither.core.solidity_types import (
  24    ArrayType,
  25    ElementaryType,
  26    FunctionType,
  27    MappingType,
  28    UserDefinedType,
  29    TypeInformation,
  30)
  31from slither.core.solidity_types.elementary_type import (
  32    Int as ElementaryTypeInt,
  33    Uint,
  34    Byte,
  35    MaxValues,
  36)
  37from slither.core.solidity_types.type import Type
  38from slither.core.solidity_types.type_alias import TypeAliasTopLevel, TypeAlias
  39from slither.core.variables.state_variable import StateVariable
  40from slither.core.variables.variable import Variable
  41from slither.slithir.exceptions import SlithIRError
  42from slither.slithir.operations import (
  43    Assignment,
  44    Binary,
  45    BinaryType,
  46    Call,
  47    Condition,
  48    Delete,
  49    EventCall,
  50    HighLevelCall,
  51    Index,
  52    InitArray,
  53    InternalCall,
  54    InternalDynamicCall,
  55    Length,
  56    LibraryCall,
  57    LowLevelCall,
  58    Member,
  59    NewArray,
  60    NewContract,
  61    NewElementaryType,
  62    NewStructure,
  63    OperationWithLValue,
  64    Return,
  65    Send,
  66    SolidityCall,
  67    Transfer,
  68    TypeConversion,
  69    Unary,
  70    Unpack,
  71    Nop,
  72    Operation,
  73)
  74from slither.slithir.operations.codesize import CodeSize
  75from slither.slithir.tmp_operations.argument import Argument, ArgumentType
  76from slither.slithir.tmp_operations.tmp_call import TmpCall
  77from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray
  78from slither.slithir.tmp_operations.tmp_new_contract import TmpNewContract
  79from slither.slithir.tmp_operations.tmp_new_elementary_type import TmpNewElementaryType
  80from slither.slithir.tmp_operations.tmp_new_structure import TmpNewStructure
  81from slither.slithir.variables import Constant, ReferenceVariable, TemporaryVariable
  82from slither.slithir.variables import TupleVariable
  83from slither.utils.function import get_function_id, get_event_id
  84from slither.utils.type import export_nested_types_from_variable
  85from slither.utils.using_for import USING_FOR
  86from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR
  87
  88if TYPE_CHECKING:
  89    from slither.core.cfg.node import Node
  90
  91logger = logging.getLogger("ConvertToIR")
  92
  93
  94def convert_expression(expression: Expression, node: "Node") -> List[Operation]:
  95    # handle standlone expression
  96    # such as return true;
  97    from slither.core.cfg.node import NodeType
  98
  99    if isinstance(expression, Literal) and node.type in [NodeType.IF, NodeType.IFLOOP]:
 100        cst = Constant(expression.value, expression.type)
 101        cond = Condition(cst)
 102        cond.set_expression(expression)
 103        cond.set_node(node)
 104        return [cond]
 105    if isinstance(expression, Identifier) and node.type in [
 106        NodeType.IF,
 107        NodeType.IFLOOP,
 108    ]:
 109        cond = Condition(expression.value)
 110        cond.set_expression(expression)
 111        cond.set_node(node)
 112        return [cond]
 113
 114    visitor = ExpressionToSlithIR(expression, node)
 115    result = visitor.result()
 116    is_solidity = node.compilation_unit.is_solidity
 117    result = apply_ir_heuristics(result, node, is_solidity)
 118
 119    if result:
 120        if node.type in [NodeType.IF, NodeType.IFLOOP]:
 121            prev = result[-1]
 122            assert isinstance(prev, (OperationWithLValue)) and prev.lvalue
 123            cond = Condition(prev.lvalue)
 124            cond.set_expression(expression)
 125            cond.set_node(node)
 126            result.append(cond)
 127        elif node.type == NodeType.RETURN:
 128            # May return None
 129            prev = result[-1]
 130            if isinstance(prev, (OperationWithLValue)):
 131                r = Return(prev.lvalue)
 132                r.set_expression(expression)
 133                r.set_node(node)
 134                result.append(r)
 135
 136    return result
 137
 138
 139###################################################################################
 140###################################################################################
 141# region Helpers
 142###################################################################################
 143###################################################################################
 144
 145
 146def is_value(ins: Operation) -> bool:
 147    if isinstance(ins, TmpCall):
 148        if isinstance(ins.ori, Member):
 149            if ins.ori.variable_right == "value":
 150                return True
 151    return False
 152
 153
 154def is_gas(ins: Operation) -> bool:
 155    if isinstance(ins, TmpCall):
 156        if isinstance(ins.ori, Member):
 157            if ins.ori.variable_right == "gas":
 158                return True
 159    return False
 160
 161
 162def _fits_under_integer(val: int, can_be_int: bool, can_be_uint: bool) -> List[str]:
 163    """
 164    Return the list of uint/int that can contain val
 165
 166    :param val:
 167    :return:
 168    """
 169    ret: List[str] = []
 170    n = 8
 171    assert can_be_int | can_be_uint
 172    while n <= 256:
 173        if can_be_uint:
 174            if val <= 2**n - 1:
 175                ret.append(f"uint{n}")
 176        if can_be_int:
 177            if val <= (2**n) / 2 - 1:
 178                ret.append(f"int{n}")
 179        n = n + 8
 180    return ret
 181
 182
 183def _fits_under_byte(val: Union[int, str]) -> List[str]:
 184    """
 185    Return the list of byte that can contain val
 186
 187    :param val:
 188    :return:
 189    """
 190
 191    # If the value is written as an int, it can only be saved in one byte size
 192    # If its a string, it can be fitted under multiple values
 193
 194    if isinstance(val, int):
 195        hex_val = hex(val)[2:]
 196        size = len(hex_val) // 2
 197        return [f"bytes{size}"]
 198    # val is a str
 199    length = len(val.encode("utf8"))
 200    return [f"bytes{f}" for f in range(length, 33)] + ["bytes"]
 201
 202
 203def _find_function_from_parameter(
 204    arguments: List[Variable], candidates: List[Function], full_comparison: bool
 205) -> Optional[Function]:
 206    """
 207    Look for a function in candidates that can be the target based on the ir's call arguments
 208
 209    Try the implicit type conversion for uint/int/bytes. Constant values can be both uint/int
 210    While variables stick to their base type, but can changed the size.
 211    If full_comparison is True it will do a comparison of all the arguments regardless if
 212    the candidate remained is one.
 213
 214    :param arguments:
 215    :param candidates:
 216    :param full_comparison:
 217    :return:
 218    """
 219    type_args: List[str]
 220    for idx, arg in enumerate(arguments):
 221        if isinstance(arg, (list,)):
 222            type_args = [f"{get_type(arg[0].type)}[{len(arg)}]"]
 223        elif isinstance(arg, Function):
 224            type_args = [arg.signature_str]
 225        else:
 226            type_args = [get_type(arg.type)]
 227
 228        arg_type = arg.type
 229        if isinstance(
 230            arg_type, ElementaryType
 231        ) and arg_type.type in ElementaryTypeInt + Uint + Byte + ["string"]:
 232            if isinstance(arg, Constant):
 233                value = arg.value
 234                can_be_uint = True
 235                can_be_int = True
 236            else:
 237                value = MaxValues[arg_type.type]
 238                can_be_uint = False
 239                can_be_int = False
 240                if arg_type.type in ElementaryTypeInt:
 241                    can_be_int = True
 242                elif arg_type.type in Uint:
 243                    can_be_uint = True
 244
 245            if arg_type.type in ElementaryTypeInt + Uint:
 246                type_args = _fits_under_integer(value, can_be_int, can_be_uint)
 247            elif value is None and arg_type.type in ["bytes", "string"]:
 248                type_args = ["bytes", "string"]
 249            else:
 250                type_args = _fits_under_byte(value)
 251                if arg_type.type == "string":
 252                    type_args += ["string"]
 253
 254        not_found = True
 255        candidates_kept: List[Function] = []
 256        for type_arg in type_args:
 257            if not not_found:
 258                break
 259            candidates_kept = []
 260            for candidate in candidates:
 261                param = get_type(candidate.parameters[idx].type)
 262                if param == type_arg:
 263                    not_found = False
 264                    candidates_kept.append(candidate)
 265
 266            if len(candidates_kept) == 1 and not full_comparison:
 267                return candidates_kept[0]
 268        candidates = candidates_kept
 269    if len(candidates) == 1:
 270        return candidates[0]
 271    return None
 272
 273
 274def is_temporary(ins: Operation) -> bool:
 275    return isinstance(
 276        ins,
 277        (Argument, TmpNewElementaryType, TmpNewContract, TmpNewArray, TmpNewStructure),
 278    )
 279
 280
 281# endregion
 282###################################################################################
 283###################################################################################
 284# region Calls modification
 285###################################################################################
 286###################################################################################
 287
 288
 289def integrate_value_gas(result: List[Operation]) -> List[Operation]:
 290    """
 291    Integrate value and gas temporary arguments to call instruction
 292    """
 293    was_changed = True
 294
 295    calls = []
 296
 297    while was_changed:
 298        # We loop until we do not find any call to value or gas
 299        was_changed = False
 300
 301        # Find all the assignments
 302        assigments = {}
 303        for i in result:
 304            if isinstance(i, OperationWithLValue) and i.lvalue:
 305                assigments[i.lvalue.name] = i
 306            if isinstance(i, TmpCall):
 307                if isinstance(i.called, Variable) and i.called.name in assigments:
 308                    ins_ori = assigments[i.called.name]
 309                    i.set_ori(ins_ori)
 310
 311        to_remove = []
 312        variable_to_replace = {}
 313
 314        # Replace call to value, gas to an argument of the real call
 315        for idx, ins in enumerate(result):
 316            # value can be shadowed, so we check that the prev ins
 317            # is an Argument
 318            if idx == 0:
 319                continue
 320            prev_ins = result[idx - 1]
 321            if is_value(ins) and isinstance(prev_ins, Argument):
 322                was_changed = True
 323                prev_ins.set_type(ArgumentType.VALUE)
 324                # Types checked by is_value
 325                prev_ins.call_id = ins.ori.variable_left.name  # type: ignore
 326                calls.append(ins.ori.variable_left)  # type: ignore
 327                to_remove.append(ins)
 328                variable_to_replace[ins.lvalue.name] = ins.ori.variable_left  # type: ignore
 329            elif is_gas(ins) and isinstance(prev_ins, Argument):
 330                was_changed = True
 331                prev_ins.set_type(ArgumentType.GAS)
 332                # Types checked by is_gas
 333                prev_ins.call_id = ins.ori.variable_left.name  # type: ignore
 334                calls.append(ins.ori.variable_left)  # type: ignore
 335                to_remove.append(ins)
 336                variable_to_replace[ins.lvalue.name] = ins.ori.variable_left  # type: ignore
 337
 338        # Remove the call to value/gas instruction
 339        result = [i for i in result if not i in to_remove]
 340
 341        # update the real call
 342        for ins in result:
 343            if isinstance(ins, TmpCall):
 344                # use of while if there redirections
 345                while ins.called.name in variable_to_replace:
 346                    was_changed = True
 347                    ins.call_id = variable_to_replace[ins.called.name].name
 348                    calls.append(ins.called)
 349                    ins.called = variable_to_replace[ins.called.name]
 350            if isinstance(ins, Argument):
 351                while ins.call_id in variable_to_replace:
 352                    was_changed = True
 353                    ins.call_id = variable_to_replace[ins.call_id].name
 354
 355    calls = list({str(c) for c in calls})
 356    idx = 0
 357    calls_d = {}
 358    for call in calls:
 359        calls_d[str(call)] = idx
 360        idx = idx + 1
 361
 362    return result
 363
 364
 365# endregion
 366###################################################################################
 367###################################################################################
 368# region Calls modification and Type propagation
 369###################################################################################
 370###################################################################################
 371
 372
 373def get_declared_param_names(
 374    ins: Union[
 375        NewStructure,
 376        NewContract,
 377        InternalCall,
 378        LibraryCall,
 379        HighLevelCall,
 380        InternalDynamicCall,
 381        EventCall,
 382    ]
 383) -> Optional[List[List[str]]]:
 384    """
 385    Given a call operation, return the list of parameter names, in the order
 386    listed in the function declaration.
 387    #### Parameters
 388    ins -
 389        The call instruction
 390    #### Possible Returns
 391    List[List[str]] -
 392        A list of list of parameters in declaration order. Note only if it's a Function there can be multiple list
 393    None -
 394        Workaround: Unable to obtain list of parameters in declaration order
 395    """
 396    if isinstance(ins, NewStructure):
 397        return [
 398            [x.name for x in ins.structure.elems_ordered if not isinstance(x.type, MappingType)]
 399        ]
 400    if isinstance(ins, (InternalCall, LibraryCall, HighLevelCall)):
 401        if isinstance(ins.function, Function):
 402            res = [[p.name for p in ins.function.parameters]]
 403            for f in ins.function.overrides:
 404                res.append([p.name for p in f.parameters])
 405            return res
 406        return None
 407    if isinstance(ins, InternalDynamicCall):
 408        return [[p.name for p in ins.function_type.params]]
 409
 410    assert isinstance(ins, (EventCall, NewContract))
 411    return None
 412
 413
 414def reorder_arguments(
 415    args: List[Variable], call_names: List[str], decl_names: List[List[str]]
 416) -> List[Variable]:
 417    """
 418    Reorder named struct constructor arguments so that they match struct declaration ordering rather
 419    than call ordering
 420    E.g. for `struct S { int x; int y; }` we reorder `S({y : 2, x : 3})` to `S(3, 2)`
 421    #### Parameters
 422    args -
 423        Arguments to constructor call, in call order
 424    names -
 425        Parameter names in call order
 426    decl_names -
 427        List of list of parameter names in declaration order
 428    #### Returns
 429    Reordered arguments to constructor call, now in declaration order
 430    """
 431    assert len(args) == len(call_names)
 432    for names in decl_names:
 433        assert len(call_names) == len(names)
 434
 435    args_ret = []
 436    index_seen = []  # Will have the correct index order
 437
 438    # We search for declaration of parameters that is fully compatible with the call arguments
 439    # that is why if a arg name is not present in the call name we break and clear index_seen
 440    # Known issue if the overridden function reuse the same parameters' name but in different positions
 441    for names in decl_names:
 442        if len(index_seen) == len(args):
 443            break
 444
 445        for n in names:
 446            try:
 447                ind = call_names.index(n)
 448                if ind in index_seen:
 449                    continue
 450            except ValueError:
 451                index_seen.clear()
 452                break
 453            index_seen.append(ind)
 454
 455    for ind in index_seen:
 456        args_ret.append(args[ind])
 457
 458    return args_ret
 459
 460
 461def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> List[Operation]:
 462    """
 463    Propagate the types variables and convert tmp call to real call operation
 464    """
 465    calls_value = {}
 466    calls_gas = {}
 467
 468    call_data = []
 469
 470    idx = 0
 471    # use of while len() as result can be modified during the iteration
 472    while idx < len(result):
 473        ins = result[idx]
 474
 475        if isinstance(ins, TmpCall):
 476            # If the node.function is a FunctionTopLevel, then it does not have a contract
 477            contract = (
 478                node.function.contract if isinstance(node.function, FunctionContract) else None
 479            )
 480            new_ins = extract_tmp_call(ins, contract)
 481            if new_ins:
 482                new_ins.set_node(ins.node)
 483                ins = new_ins
 484                result[idx] = ins
 485
 486        # If there's two consecutive type conversions, keep only the last
 487        # ARG_CALL x                             call_data = [x]
 488        # TMP_4 = CONVERT x to Fix               call_data = []
 489        # TMP_5 = CONVERT TMP_4 to uint192       call_data = [TMP_5]
 490        if isinstance(ins, TypeConversion) and ins.variable in call_data:
 491            call_data.remove(ins.variable)
 492
 493        if isinstance(ins, Argument):
 494            # In case of duplicate arguments we overwrite the value
 495            # This can happen because of addr.call.value(1).value(2)
 496            if ins.get_type() in [ArgumentType.GAS]:
 497                calls_gas[ins.call_id] = ins.argument
 498            elif ins.get_type() in [ArgumentType.VALUE]:
 499                calls_value[ins.call_id] = ins.argument
 500            else:
 501                assert ins.get_type() == ArgumentType.CALL
 502                call_data.append(ins.argument)
 503
 504        if isinstance(ins, (HighLevelCall, NewContract, InternalDynamicCall)):
 505            if ins.call_id in calls_value:
 506                ins.call_value = calls_value[ins.call_id]
 507            if ins.call_id in calls_gas and isinstance(ins, (HighLevelCall, InternalDynamicCall)):
 508                ins.call_gas = calls_gas[ins.call_id]
 509
 510        if isinstance(ins, Call) and (ins.names is not None):
 511            assert isinstance(
 512                ins,
 513                (
 514                    NewStructure,
 515                    NewContract,
 516                    InternalCall,
 517                    LibraryCall,
 518                    HighLevelCall,
 519                    InternalDynamicCall,
 520                    EventCall,
 521                ),
 522            )
 523            decl_param_names = get_declared_param_names(ins)
 524            if decl_param_names is not None:
 525                call_data = reorder_arguments(call_data, ins.names, decl_param_names)
 526
 527        if isinstance(ins, (Call, NewContract, NewStructure)):
 528            # We might have stored some arguments for libraries
 529            if ins.arguments:
 530                call_data = ins.arguments + call_data
 531            ins.arguments = call_data
 532            call_data = []
 533
 534        if is_temporary(ins):
 535            del result[idx]
 536            continue
 537
 538        new_ins = propagate_types(ins, node)
 539        if new_ins:
 540            if isinstance(new_ins, (list,)):
 541                for new_ins_ in new_ins:
 542                    new_ins_.set_node(ins.node)
 543                del result[idx]
 544                for i, ins in enumerate(new_ins):
 545                    result.insert(idx + i, ins)
 546                idx = idx + len(new_ins) - 1
 547            else:
 548                new_ins.set_node(ins.node)
 549                result[idx] = new_ins
 550        idx = idx + 1
 551    return result
 552
 553
 554def _convert_type_contract(ir: Member) -> Assignment:
 555    assert isinstance(ir.variable_left.type, TypeInformation)
 556    contract = ir.variable_left.type.type
 557
 558    scope = ir.node.file_scope
 559
 560    if ir.variable_right == "creationCode":
 561        bytecode = scope.bytecode_init(
 562            ir.node.compilation_unit.crytic_compile_compilation_unit, contract.name
 563        )
 564        assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType("bytes"))
 565        assignment.set_expression(ir.expression)
 566        assignment.set_node(ir.node)
 567        assignment.lvalue.set_type(ElementaryType("bytes"))
 568        return assignment
 569    if ir.variable_right == "runtimeCode":
 570        bytecode = scope.bytecode_runtime(
 571            ir.node.compilation_unit.crytic_compile_compilation_unit, contract.name
 572        )
 573        assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType("bytes"))
 574        assignment.set_expression(ir.expression)
 575        assignment.set_node(ir.node)
 576        assignment.lvalue.set_type(ElementaryType("bytes"))
 577        return assignment
 578    if ir.variable_right == "interfaceId":
 579        entry_points = contract.functions_entry_points
 580        interfaceId = 0
 581        for entry_point in entry_points:
 582            interfaceId = interfaceId ^ get_function_id(entry_point.full_name)
 583        assignment = Assignment(
 584            ir.lvalue,
 585            Constant(str(interfaceId), constant_type=ElementaryType("bytes4")),
 586            ElementaryType("bytes4"),
 587        )
 588        assignment.set_expression(ir.expression)
 589        assignment.set_node(ir.node)
 590        assignment.lvalue.set_type(ElementaryType("bytes4"))
 591        return assignment
 592
 593    if ir.variable_right == "name":
 594        assignment = Assignment(ir.lvalue, Constant(contract.name), ElementaryType("string"))
 595        assignment.set_expression(ir.expression)
 596        assignment.set_node(ir.node)
 597        assignment.lvalue.set_type(ElementaryType("string"))
 598        return assignment
 599
 600    raise SlithIRError(f"type({contract.name}).{ir.variable_right} is unknown")
 601
 602
 603def propagate_types(ir: Operation, node: "Node"):  # pylint: disable=too-many-locals
 604    # propagate the type
 605    node_function = node.function
 606
 607    using_for: USING_FOR = {}
 608    if isinstance(node_function, FunctionContract):
 609        using_for = node_function.contract.using_for_complete
 610    elif isinstance(node_function, FunctionTopLevel):
 611        using_for = node_function.using_for_complete
 612
 613    if isinstance(ir, OperationWithLValue) and ir.lvalue:
 614        # Force assignment in case of missing previous correct type
 615        if not ir.lvalue.type:
 616            if isinstance(ir, Assignment):
 617                ir.lvalue.set_type(ir.rvalue.type)
 618            elif isinstance(ir, Binary):
 619                if BinaryType.return_bool(ir.type):
 620                    ir.lvalue.set_type(ElementaryType("bool"))
 621                else:
 622                    ir.lvalue.set_type(ir.variable_left.type)
 623            elif isinstance(ir, Delete):
 624                # nothing to propagate
 625                pass
 626            elif isinstance(ir, LibraryCall):
 627                return convert_type_library_call(ir, ir.destination)
 628            elif isinstance(ir, HighLevelCall):
 629                t = ir.destination.type
 630                # Temporary operation (they are removed later)
 631                if t is None:
 632                    return None
 633
 634                if isinstance(t, ElementaryType) and t.name == "address":
 635                    if can_be_solidity_func(ir):
 636                        return convert_to_solidity_func(ir)
 637
 638                # convert library or top level function
 639                if t in using_for or "*" in using_for:
 640                    new_ir = convert_to_library_or_top_level(ir, node, using_for)
 641                    if new_ir:
 642                        return new_ir
 643
 644                # convert library function when used with "this"
 645                if (
 646                    isinstance(t, ElementaryType)
 647                    and t.name == "address"
 648                    and ir.destination.name == "this"
 649                    and UserDefinedType(node_function.contract) in using_for
 650                ):
 651                    new_ir = convert_to_library_or_top_level(ir, node, using_for)
 652                    if new_ir:
 653                        return new_ir
 654
 655                if isinstance(t, UserDefinedType):
 656                    # UserdefinedType
 657                    t_type = t.type
 658                    if isinstance(t_type, Contract):
 659                        # the target contract of the IR is the t_type (the destination of the call)
 660                        return convert_type_of_high_and_internal_level_call(ir, t_type)
 661
 662                # Convert HighLevelCall to LowLevelCall
 663                if (isinstance(t, ElementaryType) and t.name == "address") or (
 664                    isinstance(t, TypeAlias) and t.underlying_type.name == "address"
 665                ):
 666                    if ir.destination.name == "this":
 667                        # Cannot be a top level function with this.
 668                        assert isinstance(node_function, FunctionContract)
 669                        # the target contract is the contract itself
 670                        return convert_type_of_high_and_internal_level_call(
 671                            ir, node_function.contract
 672                        )
 673                    if can_be_low_level(ir):
 674                        return convert_to_low_level(ir)
 675
 676                # Convert push operations
 677                # May need to insert a new operation
 678                # Which leads to return a list of operation
 679                if isinstance(t, ArrayType) or (
 680                    isinstance(t, ElementaryType) and t.type == "bytes"
 681                ):
 682                    # Solidity uses push
 683                    # Vyper uses append
 684                    if ir.function_name in ["push", "append"] and len(ir.arguments) <= 1:
 685                        return convert_to_push(ir, node)
 686                    if ir.function_name == "pop" and len(ir.arguments) == 0:
 687                        return convert_to_pop(ir, node)
 688
 689            elif isinstance(ir, Index):
 690                if isinstance(ir.variable_left.type, MappingType):
 691                    ir.lvalue.set_type(ir.variable_left.type.type_to)
 692                elif isinstance(ir.variable_left.type, ArrayType):
 693                    ir.lvalue.set_type(ir.variable_left.type.type)
 694
 695            elif isinstance(ir, InitArray):
 696                length = len(ir.init_values)
 697                t = ir.init_values[0].type
 698                ir.lvalue.set_type(ArrayType(t, length))
 699            elif isinstance(ir, InternalCall):
 700                # if its not a tuple, return a singleton
 701                if ir.function is None:
 702                    func = node.function
 703                    function_contract = (
 704                        func.contract if isinstance(func, FunctionContract) else None
 705                    )
 706                    # the target contract might be None if its a top level function
 707                    convert_type_of_high_and_internal_level_call(ir, function_contract)
 708                return_type = ir.function.return_type
 709                if return_type:
 710                    if len(return_type) == 1:
 711                        ir.lvalue.set_type(return_type[0])
 712                    elif len(return_type) > 1:
 713                        ir.lvalue.set_type(return_type)
 714                else:
 715                    ir.lvalue = None
 716            elif isinstance(ir, InternalDynamicCall):
 717                # if its not a tuple, return a singleton
 718                return_type = ir.function_type.return_type
 719                if return_type:
 720                    if len(return_type) == 1:
 721                        ir.lvalue.set_type(return_type[0])
 722                    else:
 723                        ir.lvalue.set_type(return_type)
 724                else:
 725                    ir.lvalue = None
 726            elif isinstance(ir, LowLevelCall):
 727                # Call are not yet converted
 728                # This should not happen
 729                assert False
 730            elif isinstance(ir, Member):
 731                # TODO we should convert the reference to a temporary if the member is a length or a balance
 732                if (
 733                    ir.variable_right == "length"
 734                    and not isinstance(ir.variable_left, Contract)
 735                    and isinstance(ir.variable_left.type, (ElementaryType, ArrayType))
 736                ):
 737                    new_length = Length(ir.variable_left, ir.lvalue)
 738                    assert ir.expression
 739                    new_length.set_expression(ir.expression)
 740                    new_length.lvalue.points_to = ir.variable_left
 741                    new_length.set_node(ir.node)
 742                    return new_length
 743                # This only happen for .balance/code/codehash access on a variable for which we dont know at
 744                # early parsing time the type
 745                # Like
 746                # function return_addr() internal returns(addresss)
 747                #
 748                # return_addr().balance
 749                # Here slithIR will incorrectly create a REF variable instead of a Temp variable
 750                # However this pattern does not appear so often
 751                if (
 752                    ir.variable_right.name in ["balance", "code", "codehash"]
 753                    and not isinstance(ir.variable_left, Contract)
 754                    and isinstance(ir.variable_left.type, ElementaryType)
 755                ):
 756                    name = ir.variable_right.name + "(address)"
 757                    sol_func = SolidityFunction(name)
 758                    s = SolidityCall(
 759                        sol_func,
 760                        1,
 761                        ir.lvalue,
 762                        sol_func.return_type,
 763                    )
 764                    s.arguments.append(ir.variable_left)
 765                    s.set_expression(ir.expression)
 766                    s.lvalue.set_type(sol_func.return_type)
 767                    s.set_node(ir.node)
 768                    return s
 769                if (
 770                    ir.variable_right == "codesize"
 771                    and not isinstance(ir.variable_left, Contract)
 772                    and isinstance(ir.variable_left.type, ElementaryType)
 773                ):
 774                    b = CodeSize(ir.variable_left, ir.lvalue)
 775                    b.set_expression(ir.expression)
 776                    b.set_node(ir.node)
 777                    return b
 778                if ir.variable_right == "selector" and isinstance(ir.variable_left, (CustomError)):
 779                    assignment = Assignment(
 780                        ir.lvalue,
 781                        Constant(
 782                            str(get_function_id(ir.variable_left.solidity_signature)),
 783                            ElementaryType("bytes4"),
 784                        ),
 785                        ElementaryType("bytes4"),
 786                    )
 787                    assignment.set_expression(ir.expression)
 788                    assignment.set_node(ir.node)
 789                    assignment.lvalue.set_type(ElementaryType("bytes4"))
 790                    return assignment
 791
 792                if isinstance(ir.variable_right, (CustomError)):
 793                    assignment = Assignment(
 794                        ir.lvalue,
 795                        Constant(
 796                            str(get_function_id(ir.variable_left.solidity_signature)),
 797                            ElementaryType("bytes4"),
 798                        ),
 799                        ElementaryType("bytes4"),
 800                    )
 801                    assignment.set_expression(ir.expression)
 802                    assignment.set_node(ir.node)
 803                    assignment.lvalue.set_type(ElementaryType("bytes4"))
 804                    return assignment
 805                if ir.variable_right == "selector" and isinstance(ir.variable_left, (Event)):
 806                    # the event selector returns a bytes32, which is different from the error/function selector
 807                    # which returns a bytes4
 808                    assignment = Assignment(
 809                        ir.lvalue,
 810                        Constant(
 811                            str(get_event_id(ir.variable_left.full_name)), ElementaryType("bytes32")
 812                        ),
 813                        ElementaryType("bytes32"),
 814                    )
 815                    assignment.set_expression(ir.expression)
 816                    assignment.set_node(ir.node)
 817                    assignment.lvalue.set_type(ElementaryType("bytes32"))
 818                    return assignment
 819                if ir.variable_right == "selector" and (
 820                    isinstance(ir.variable_left.type, (Function))
 821                ):
 822                    assignment = Assignment(
 823                        ir.lvalue,
 824                        Constant(
 825                            str(get_function_id(ir.variable_left.type.full_name)),
 826                            ElementaryType("bytes4"),
 827                        ),
 828                        ElementaryType("bytes4"),
 829                    )
 830                    assignment.set_expression(ir.expression)
 831                    assignment.set_node(ir.node)
 832                    assignment.lvalue.set_type(ElementaryType("bytes4"))
 833                    return assignment
 834                if isinstance(ir.variable_left, TemporaryVariable) and isinstance(
 835                    ir.variable_left.type, TypeInformation
 836                ):
 837                    return _convert_type_contract(ir)
 838                left = ir.variable_left
 839                t = None
 840                ir_func = ir.node.function
 841                # Handling of this.function_name usage
 842                if (
 843                    left == SolidityVariable("this")
 844                    and isinstance(ir.variable_right, Constant)
 845                    and isinstance(ir_func, FunctionContract)
 846                    and str(ir.variable_right) in [x.name for x in ir_func.contract.functions]
 847                ):
 848                    # Assumption that this.function_name can only compile if
 849                    # And the contract does not have two functions starting with function_name
 850                    # Otherwise solc raises:
 851                    # Error: Member "f" not unique after argument-dependent lookup in contract
 852                    targeted_function = next(
 853                        (x for x in ir_func.contract.functions if x.name == str(ir.variable_right))
 854                    )
 855                    ir.lvalue.set_type(targeted_function)
 856                elif isinstance(left, (Variable, SolidityVariable)):
 857                    t = left.type
 858                elif isinstance(left, (Contract, Enum, Structure)):
 859                    t = UserDefinedType(left)
 860                # can be None due to temporary operation
 861                if t:
 862                    if isinstance(t, UserDefinedType):
 863                        # UserdefinedType
 864                        type_t = t.type
 865                        if isinstance(type_t, Enum):
 866                            ir.lvalue.set_type(t)
 867                        elif isinstance(type_t, Structure):
 868                            elems = type_t.elems
 869                            for elem in elems:
 870                                if elem == ir.variable_right:
 871                                    ir.lvalue.set_type(elems[elem].type)
 872                        else:
 873                            assert isinstance(type_t, Contract)
 874                            # Allow type propagation as a Function
 875                            # Only for reference variables
 876                            # This allows to track the selector keyword
 877                            # We don't need to check for function collision, as solc prevents the use of selector
 878                            # if there are multiple functions with the same name
 879                            f = next(
 880                                (f for f in type_t.functions if f.name == ir.variable_right),
 881                                None,
 882                            )
 883                            if f:
 884                                ir.lvalue.set_type(f)
 885                            else:
 886                                # Allow propagation for variable access through contract's name
 887                                # like Base_contract.my_variable
 888                                v = next(
 889                                    (
 890                                        v
 891                                        for v in type_t.state_variables
 892                                        if v.name == ir.variable_right
 893                                    ),
 894                                    None,
 895                                )
 896                                if v:
 897                                    ir.lvalue.set_type(v.type)
 898            elif isinstance(ir, NewArray):
 899                ir.lvalue.set_type(ir.array_type)
 900            elif isinstance(ir, NewContract):
 901                ir.lvalue.set_type(ir.contract_name)
 902            elif isinstance(ir, NewElementaryType):
 903                ir.lvalue.set_type(ir.type)
 904            elif isinstance(ir, NewStructure):
 905                ir.lvalue.set_type(UserDefinedType(ir.structure))
 906            elif isinstance(ir, Send):
 907                ir.lvalue.set_type(ElementaryType("bool"))
 908            elif isinstance(ir, SolidityCall):
 909                if ir.function.name in ["type(address)", "type()"]:
 910                    ir.function.return_type = [TypeInformation(ir.arguments[0])]
 911                return_type = ir.function.return_type
 912                if len(return_type) == 1:
 913                    ir.lvalue.set_type(return_type[0])
 914                elif len(return_type) > 1:
 915                    ir.lvalue.set_type(return_type)
 916            elif isinstance(ir, TypeConversion):
 917                ir.lvalue.set_type(ir.type)
 918            elif isinstance(ir, Unary):
 919                ir.lvalue.set_type(ir.rvalue.type)
 920            elif isinstance(ir, Unpack):
 921                types = ir.tuple.type.type
 922                idx = ir.index
 923                t = types[idx]
 924                ir.lvalue.set_type(t)
 925            elif isinstance(
 926                ir,
 927                (
 928                    Argument,
 929                    TmpCall,
 930                    TmpNewArray,
 931                    TmpNewContract,
 932                    TmpNewStructure,
 933                    TmpNewElementaryType,
 934                ),
 935            ):
 936                # temporary operation; they will be removed
 937                pass
 938            else:
 939                raise SlithIRError(f"Not handling {type(ir)} during type propagation")
 940    return None
 941
 942
 943# pylint: disable=too-many-locals
 944def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, Nop]:
 945    assert isinstance(ins, TmpCall)
 946    if isinstance(ins.called, Variable) and isinstance(ins.called.type, FunctionType):
 947        # If the call is made to a variable member, where the member is this
 948        # We need to convert it to a HighLelelCall and not an internal dynamic call
 949        if isinstance(ins.ori, Member) and ins.ori.variable_left == SolidityVariable("this"):
 950            pass
 951        else:
 952            call = InternalDynamicCall(ins.lvalue, ins.called, ins.called.type)
 953            call.set_expression(ins.expression)
 954            call.call_id = ins.call_id
 955            return call
 956    if isinstance(ins.ori, Member):
 957        # If there is a call on an inherited contract, it is an internal call or an event
 958        if contract and ins.ori.variable_left in contract.inheritance + [contract]:
 959            if str(ins.ori.variable_right) in [f.name for f in contract.functions]:
 960                internalcall = InternalCall(
 961                    (ins.ori.variable_right, ins.ori.variable_left.name),
 962                    ins.nbr_arguments,
 963                    ins.lvalue,
 964                    ins.type_call,
 965                )
 966                internalcall.set_expression(ins.expression)
 967                internalcall.call_id = ins.call_id
 968                return internalcall
 969            if str(ins.ori.variable_right) in [f.name for f in contract.events]:
 970                eventcall = EventCall(ins.ori.variable_right)
 971                eventcall.set_expression(ins.expression)
 972                eventcall.call_id = ins.call_id
 973                return eventcall
 974        if isinstance(ins.ori.variable_left, Contract):
 975            st = ins.ori.variable_left.get_structure_from_name(ins.ori.variable_right)
 976            if st:
 977                op = NewStructure(st, ins.lvalue, names=ins.names)
 978                op.set_expression(ins.expression)
 979                op.call_id = ins.call_id
 980                return op
 981
 982            # For event emit through library
 983            # lib L { event E()}
 984            # ...
 985            # emit L.E();
 986            if str(ins.ori.variable_right) in [f.name for f in ins.ori.variable_left.events]:
 987                eventcall = EventCall(ins.ori.variable_right)
 988                eventcall.set_expression(ins.expression)
 989                eventcall.call_id = ins.call_id
 990                return eventcall
 991
 992            # lib Lib { error Error()} ... revert Lib.Error()
 993            if str(ins.ori.variable_right) in ins.ori.variable_left.custom_errors_as_dict:
 994                custom_error = ins.ori.variable_left.custom_errors_as_dict[
 995                    str(ins.ori.variable_right)
 996                ]
 997                assert isinstance(
 998                    custom_error,
 999                    CustomError,
1000                )
1001                sol_function = SolidityCustomRevert(custom_error)
1002                solidity_call = SolidityCall(
1003                    sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call
1004                )
1005                solidity_call.set_expression(ins.expression)
1006                return solidity_call
1007
1008            libcall = LibraryCall(
1009                ins.ori.variable_left,
1010                ins.ori.variable_right,
1011                ins.nbr_arguments,
1012                ins.lvalue,
1013                ins.type_call,
1014                names=ins.names,
1015            )
1016            libcall.set_expression(ins.expression)
1017            libcall.call_id = ins.call_id
1018            return libcall
1019        if isinstance(ins.ori.variable_left, Function):
1020            # Support for library call where the parameter is a function
1021            # We could merge this with the standard library handling
1022            # Except that we will have some troubles with using_for
1023            # As the type of the function will not match function()
1024            # Additionally we do not have a correct view on the parameters of the tmpcall
1025            # At this level
1026            #
1027            # library FunctionExtensions {
1028            #     function h(function() internal _t, uint8) internal {  }
1029            # }
1030            # contract FunctionMembers {
1031            #     using FunctionExtensions for function();
1032            #
1033            #     function f() public {
1034            #         f.h(1);
1035            #     }
1036            # }
1037            node_func = ins.node.function
1038            using_for = (
1039                node_func.contract.using_for_complete
1040                if isinstance(node_func, FunctionContract)
1041                else {}
1042            )
1043
1044            targeted_libraries = (
1045                [] + using_for.get("*", []) + using_for.get(FunctionType([], []), [])
1046            )
1047            lib_contract: Contract
1048            candidates = []
1049            for lib_contract_type in targeted_libraries:
1050                if not isinstance(lib_contract_type, UserDefinedType) and isinstance(
1051                    lib_contract_type.type, Contract
1052                ):
1053                    continue
1054                if isinstance(lib_contract_type, FunctionContract):
1055                    # Using for with list of functions, this is the function called
1056                    candidates.append(lib_contract_type)
1057                else:
1058                    lib_contract = lib_contract_type.type
1059                    for lib_func in lib_contract.functions:
1060                        if lib_func.name == ins.ori.variable_right:
1061                            candidates.append(lib_func)
1062
1063            if len(candidates) == 1:
1064                lib_func = candidates[0]
1065                # Library must be from a contract
1066                assert isinstance(lib_func, FunctionContract)
1067                lib_call = LibraryCall(
1068                    lib_func.contract,
1069                    Constant(lib_func.name),
1070                    len(lib_func.parameters),
1071                    ins.lvalue,
1072                    "d",
1073                    names=ins.names,
1074                )
1075                lib_call.set_expression(ins.expression)
1076                lib_call.set_node(ins.node)
1077                lib_call.call_gas = ins.call_gas
1078                lib_call.call_id = ins.call_id
1079                lib_call.set_node(ins.node)
1080                lib_call.function = lib_func
1081                lib_call.arguments.append(ins.ori.variable_left)
1082                return lib_call
1083            # We do not support something lik
1084            # library FunctionExtensions {
1085            #     function h(function() internal _t, uint8) internal {  }
1086            #     function h(function() internal _t, bool) internal {  }
1087            # }
1088            # contract FunctionMembers {
1089            #     using FunctionExtensions for function();
1090            #
1091            #     function f() public {
1092            #         f.h(1);
1093            #     }
1094            # }
1095            to_log = "Slither does not support dynamic functions to libraries if functions have the same name"
1096            to_log += f"{[candidate.full_name for candidate in candidates]}"
1097            raise SlithIRError(to_log)
1098        if isinstance(ins.ori.variable_left, SolidityImportPlaceHolder):
1099            # For top level import, where the import statement renames the filename
1100            # See https://blog.soliditylang.org/2020/09/02/solidity-0.7.1-release-announcement/
1101
1102            current_path = Path(ins.ori.variable_left.source_mapping.filename.absolute).parent
1103            target = str(
1104                Path(current_path, ins.ori.variable_left.import_directive.filename).absolute()
1105            )
1106            top_level_function_targets = [
1107                f
1108                for f in ins.compilation_unit.functions_top_level
1109                if f.source_mapping.filename.absolute == target
1110                and f.name == ins.ori.variable_right
1111                and len(f.parameters) == ins.nbr_arguments
1112            ]
1113
1114            internalcall = InternalCall(
1115                (ins.ori.variable_right, ins.ori.variable_left.name),
1116                ins.nbr_arguments,
1117                ins.lvalue,
1118                ins.type_call,
1119            )
1120            internalcall.set_expression(ins.expression)
1121            internalcall.call_id = ins.call_id
1122            internalcall.function_candidates = top_level_function_targets
1123            return internalcall
1124
1125        if ins.ori.variable_left == ElementaryType("bytes") and ins.ori.variable_right == Constant(
1126            "concat"
1127        ):
1128            s = SolidityCall(
1129                SolidityFunction("bytes.concat()"),
1130                ins.nbr_arguments,
1131                ins.lvalue,
1132                ins.type_call,
1133            )
1134            s.set_expression(ins.expression)
1135            return s
1136
1137        if ins.ori.variable_left == ElementaryType("string") and ins.ori.variable_right == Constant(
1138            "concat"
1139        ):
1140            s = SolidityCall(
1141                SolidityFunction("string.concat()"),
1142                ins.nbr_arguments,
1143                ins.lvalue,
1144                ins.type_call,
1145            )
1146            s.set_expression(ins.expression)
1147            return s
1148
1149        msgcall = HighLevelCall(
1150            ins.ori.variable_left,
1151            ins.ori.variable_right,
1152            ins.nbr_arguments,
1153            ins.lvalue,
1154            ins.type_call,
1155            names=ins.names,
1156        )
1157        msgcall.call_id = ins.call_id
1158
1159        if ins.call_gas:
1160            msgcall.call_gas = ins.call_gas
1161        if ins.call_value:
1162            msgcall.call_value = ins.call_value
1163        msgcall.set_expression(ins.expression)
1164
1165        return msgcall
1166
1167    if isinstance(ins.ori, TmpCall):
1168        r = extract_tmp_call(ins.ori, contract)
1169        r.set_node(ins.node)
1170        return r
1171    if isinstance(ins.called, SolidityVariableComposed):
1172        if str(ins.called) == "block.blockhash":
1173            ins.called = SolidityFunction("blockhash(uint256)")
1174
1175    if isinstance(ins.called, SolidityFunction):
1176        s = SolidityCall(ins.called, ins.nbr_arguments, ins.lvalue, ins.type_call)
1177        s.set_expression(ins.expression)
1178        return s
1179
1180    if isinstance(ins.called, CustomError):
1181        sol_function = SolidityCustomRevert(ins.called)
1182        s = SolidityCall(sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call)
1183        s.set_expression(ins.expression)
1184        return s
1185
1186    if isinstance(ins.ori, TmpNewElementaryType):
1187        n = NewElementaryType(ins.ori.type, ins.lvalue)
1188        n.set_expression(ins.expression)
1189        return n
1190
1191    if isinstance(ins.ori, TmpNewContract):
1192        op = NewContract(ins.ori.contract_name, ins.lvalue)
1193        op.set_expression(ins.expression)
1194        op.call_id = ins.call_id
1195        if ins.call_value:
1196            op.call_value = ins.call_value
1197        if ins.call_salt:
1198            op.call_salt = ins.call_salt
1199        return op
1200
1201    if isinstance(ins.ori, TmpNewArray):
1202        n = NewArray(ins.ori.array_type, ins.lvalue)
1203        n.set_expression(ins.expression)
1204        return n
1205
1206    if isinstance(ins.called, Structure):
1207        op = NewStructure(ins.called, ins.lvalue, names=ins.names)
1208        op.set_expression(ins.expression)
1209        op.call_id = ins.call_id
1210        op.set_expression(ins.expression)
1211        return op
1212
1213    if isinstance(ins.called, Event):
1214        e = EventCall(ins.called.name)
1215        e.set_expression(ins.expression)
1216        return e
1217
1218    if isinstance(ins.called, Contract):
1219        # Called a base constructor, where there is no constructor
1220        if ins.called.constructor is None:
1221            return Nop()
1222        # Case where:
1223        # contract A{ constructor(uint) }
1224        # contract B is A {}
1225        # contract C is B{ constructor() A(10) B() {}
1226        # C calls B(), which does not exist
1227        # Ideally we should compare here for the parameters types too
1228        if len(ins.called.constructor.parameters) != ins.nbr_arguments:
1229            return Nop()
1230        internalcall = InternalCall(
1231            ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call, ins.names
1232        )
1233        internalcall.call_id = ins.call_id
1234        internalcall.set_expression(ins.expression)
1235        return internalcall
1236
1237    raise SlithIRError(f"Not extracted {type(ins.called)} {ins}")
1238
1239
1240# endregion
1241###################################################################################
1242###################################################################################
1243# region Conversion operations
1244###################################################################################
1245###################################################################################
1246
1247
1248def can_be_low_level(ir: HighLevelCall) -> bool:
1249    return ir.function_name in [
1250        "transfer",
1251        "send",
1252        "call",
1253        "delegatecall",
1254        "callcode",
1255        "staticcall",
1256        "raw_call",
1257    ]
1258
1259
1260def convert_to_low_level(
1261    ir: HighLevelCall,
1262) -> Union[Send, LowLevelCall, Transfer,]:
1263    """
1264    Convert to a transfer/send/or low level call
1265    The function assume to receive a correct IR
1266    The checks must be done by the caller
1267
1268    Must be called after can_be_low_level
1269    """
1270    if ir.function_name == "transfer":
1271        assert len(ir.arguments) == 1
1272        prev_ir = ir
1273        ir = Transfer(ir.destination, ir.arguments[0])
1274        ir.set_expression(prev_ir.expression)
1275        ir.set_node(prev_ir.node)
1276        return ir
1277    if ir.function_name == "send":
1278        assert len(ir.arguments) == 1
1279        prev_ir = ir
1280        ir = Send(ir.destination, ir.arguments[0], ir.lvalue)
1281        ir.set_expression(prev_ir.expression)
1282        ir.set_node(prev_ir.node)
1283        ir.lvalue.set_type(ElementaryType("bool"))
1284        return ir
1285    if ir.function_name in ["call", "delegatecall", "callcode", "staticcall", "raw_call"]:
1286        new_ir = LowLevelCall(
1287            ir.destination, ir.function_name, ir.nbr_arguments, ir.lvalue, ir.type_call
1288        )
1289        new_ir.call_gas = ir.call_gas
1290        new_ir.call_value = ir.call_value
1291        new_ir.arguments = ir.arguments
1292        # TODO fix this for Vyper
1293        if ir.node.compilation_unit.solc_version >= "0.5":
1294            new_ir.lvalue.set_type([ElementaryType("bool"), ElementaryType("bytes")])
1295        else:
1296            new_ir.lvalue.set_type(ElementaryType("bool"))
1297        new_ir.set_expression(ir.expression)
1298        new_ir.set_node(ir.node)
1299        return new_ir
1300    raise SlithIRError(f"Incorrect conversion to low level {ir}")
1301
1302
1303def can_be_solidity_func(ir: HighLevelCall) -> bool:
1304    if not isinstance(ir, HighLevelCall):
1305        return False
1306    return ir.destination.name == "abi" and ir.function_name in [
1307        "encode",
1308        "encodePacked",
1309        "encodeWithSelector",
1310        "encodeWithSignature",
1311        "encodeCall",
1312        "decode",
1313    ]
1314
1315
1316def convert_to_solidity_func(
1317    ir: HighLevelCall,
1318) -> SolidityCall:
1319    """
1320    Must be called after can_be_solidity_func
1321    :param ir:
1322    :return:
1323    """
1324    call = SolidityFunction(f"abi.{ir.function_name}()")
1325    new_ir = SolidityCall(call, ir.nbr_arguments, ir.lvalue, ir.type_call)
1326    new_ir.arguments = ir.arguments
1327    new_ir.set_expression(ir.expression)
1328    new_ir.set_node(ir.node)
1329    if isinstance(call.return_type, list) and len(call.return_type) == 1:
1330        new_ir.lvalue.set_type(call.return_type[0])
1331    elif (
1332        isinstance(new_ir.lvalue, TupleVariable)
1333        and call == SolidityFunction("abi.decode()")
1334        and len(new_ir.arguments) == 2
1335        and isinstance(new_ir.arguments[1], list)
1336    ):
1337        types = []
1338        for arg_type in new_ir.arguments[1]:
1339            decode_type = arg_type
1340            if isinstance(decode_type, (Structure, Enum, Contract)):
1341                decode_type = UserDefinedType(decode_type)
1342            types.append(decode_type)
1343        new_ir.lvalue.set_type(types)
1344    # abi.decode where the type to decode is a singleton
1345    # abi.decode(a, (uint))
1346    elif call == SolidityFunction("abi.decode()") and len(new_ir.arguments) == 2:
1347        # If the variable is a referenceVariable, we are lost
1348        # See https://github.com/crytic/slither/issues/566 for potential solutions
1349        if not isinstance(new_ir.arguments[1], ReferenceVariable):
1350            decode_type = new_ir.arguments[1]
1351            if isinstance(decode_type, (Structure, Enum, Contract)):
1352                decode_type = UserDefinedType(decode_type)
1353            new_ir.lvalue.set_type(decode_type)
1354    else:
1355        new_ir.lvalue.set_type(call.return_type)
1356    return new_ir
1357
1358
1359def convert_to_push_expand_arr(
1360    ir: HighLevelCall, node: "Node", ret: List[Any]
1361) -> TemporaryVariable:
1362    arr = ir.destination
1363
1364    length = ReferenceVariable(node)
1365    length.set_type(ElementaryType("uint256"))
1366
1367    ir_length = Length(arr, length)
1368    ir_length.set_expression(ir.expression)
1369    ir_length.set_node(ir.node)
1370    ir_length.lvalue.points_to = arr
1371    ret.append(ir_length)
1372
1373    length_val = TemporaryVariable(node)
1374    length_val.set_type(ElementaryType("uint256"))
1375    ir_get_length = Assignment(length_val, length, ElementaryType("uint256"))
1376    ir_get_length.set_expression(ir.expression)
1377    ir_get_length.set_node(ir.node)
1378    ret.append(ir_get_length)
1379
1380    new_length_val = TemporaryVariable(node)
1381    ir_add_1 = Binary(
1382        new_length_val, length_val, Constant("1", ElementaryType("uint256")), BinaryType.ADDITION
1383    )
1384    ir_add_1.set_expression(ir.expression)
1385    ir_add_1.set_node(ir.node)
1386    ret.append(ir_add_1)
1387
1388    ir_assign_length = Assignment(length, new_length_val, ElementaryType("uint256"))
1389    ir_assign_length.set_expression(ir.expression)
1390    ir_assign_length.set_node(ir.node)
1391    ret.append(ir_assign_length)
1392
1393    return length_val
1394
1395
1396def convert_to_push_set_val(
1397    ir: HighLevelCall,
1398    node: "Node",
1399    length_val: TemporaryVariable,
1400    ret: List[
1401        Union[
1402            Length,
1403            Assignment,
1404            Binary,
1405        ]
1406    ],
1407) -> None:
1408    arr = ir.destination
1409
1410    new_type = ir.destination.type.type
1411
1412    element_to_add = ReferenceVariable(node)
1413    element_to_add.set_type(new_type)
1414    ir_assign_element_to_add = Index(element_to_add, arr, length_val)
1415    ir_assign_element_to_add.set_expression(ir.expression)
1416    ir_assign_element_to_add.set_node(ir.node)
1417    ret.append(ir_assign_element_to_add)
1418
1419    if len(ir.arguments) > 0:
1420        assign_value = ir.arguments[0]
1421        if isinstance(assign_value, list):
1422            assign_value = TemporaryVariable(node)
1423            assign_value.set_type(element_to_add.type)
1424            ir_assign_value = InitArray(ir.arguments[0], assign_value)
1425            ir_assign_value.set_expression(ir.expression)
1426            ir_assign_value.set_node(ir.node)
1427            ret.append(ir_assign_value)
1428
1429        ir_assign_value = Assignment(element_to_add, assign_value, assign_value.type)
1430        ir_assign_value.set_expression(ir.expression)
1431        ir_assign_value.set_node(ir.node)
1432        ret.append(ir_assign_value)
1433    else:
1434        new_element = ir.lvalue
1435        new_element.set_type(new_type)
1436        ir_assign_value = Assignment(new_element, element_to_add, new_type)
1437        ir_assign_value.set_expression(ir.expression)
1438        ir_assign_value.set_node(ir.node)
1439        ret.append(ir_assign_value)
1440
1441
1442def convert_to_push(
1443    ir: HighLevelCall, node: "Node"
1444) -> List[Union[Length, Assignment, Binary, Index, InitArray,]]:
1445    """
1446    Convert a call to a series of operations to push a new value onto the array
1447
1448    The function assume to receive a correct IR
1449    The checks must be done by the caller
1450
1451    May necessitate to create an intermediate operation (InitArray)
1452    Necessitate to return the length (see push documentation)
1453    As a result, the function return may return a list
1454    """
1455
1456    ret = []
1457
1458    length_val = convert_to_push_expand_arr(ir, node, ret)
1459    convert_to_push_set_val(ir, node, length_val, ret)
1460
1461    return ret
1462
1463
1464def convert_to_pop(ir: HighLevelCall, node: "Node") -> List[Operation]:
1465    """
1466    Convert pop operators
1467    Return a list of 6 operations
1468    """
1469
1470    ret: List[Operation] = []
1471
1472    arr = ir.destination
1473    length = ReferenceVariable(node)
1474    length.set_type(ElementaryType("uint256"))
1475
1476    ir_length = Length(arr, length)
1477    assert ir.expression
1478    ir_length.set_expression(ir.expression)
1479    ir_length.set_node(ir.node)
1480    length.points_to = arr
1481    ret.append(ir_length)
1482
1483    val = TemporaryVariable(node)
1484
1485    ir_sub_1 = Binary(val, length, Constant("1", ElementaryType("uint256")), BinaryType.SUBTRACTION)
1486    ir_sub_1.set_expression(ir.expression)
1487    ir_sub_1.set_node(ir.node)
1488    ret.append(ir_sub_1)
1489
1490    element_to_delete = ReferenceVariable(node)
1491    ir_assign_element_to_delete = Index(element_to_delete, arr, val)
1492    # TODO the following is equivalent to length.points_to = arr
1493    # Should it be removed?
1494    ir_length.lvalue.points_to = arr
1495    # Note bytes is an ElementaryType not ArrayType and bytes1 should be returned
1496    # since bytes is bytes1[] without padding between the elements
1497    # while in other cases such as uint256[] (ArrayType) we use ir.destination.type.type
1498    # in this way we will have the type always set to the corresponding ElementaryType
1499    element_to_delete.set_type(
1500        ElementaryType("bytes1")
1501        if isinstance(ir.destination.type, ElementaryType)
1502        else ir.destination.type.type
1503    )
1504    ir_assign_element_to_delete.set_expression(ir.expression)
1505    ir_assign_element_to_delete.set_node(ir.node)
1506    ret.append(ir_assign_element_to_delete)
1507
1508    ir_delete = Delete(element_to_delete, element_to_delete)
1509    ir_delete.set_expression(ir.expression)
1510    ir_delete.set_node(ir.node)
1511    ret.append(ir_delete)
1512
1513    length_to_assign = ReferenceVariable(node)
1514    length_to_assign.set_type(ElementaryType("uint256"))
1515    ir_length = Length(arr, length_to_assign)
1516    ir_length.set_expression(ir.expression)
1517    length_to_assign.points_to = arr
1518    ir_length.set_node(ir.node)
1519    ret.append(ir_length)
1520
1521    ir_assign_length = Assignment(length_to_assign, val, ElementaryType("uint256"))
1522    ir_assign_length.set_expression(ir.expression)
1523    ir_assign_length.set_node(ir.node)
1524    ret.append(ir_assign_length)
1525
1526    return ret
1527
1528
1529def look_for_library_or_top_level(
1530    ir: HighLevelCall,
1531    using_for,
1532    t: Union[
1533        UserDefinedType,
1534        ElementaryType,
1535        str,
1536        TypeAliasTopLevel,
1537    ],
1538) -> Optional[Union[LibraryCall, InternalCall,]]:
1539    for destination in using_for[t]:
1540        if isinstance(destination, FunctionTopLevel) and destination.name == ir.function_name:
1541            arguments = [ir.destination] + ir.arguments
1542            if (
1543                len(destination.parameters) == len(arguments)
1544                and _find_function_from_parameter(arguments, [destination], True) is not None
1545            ):
1546                internalcall = InternalCall(destination, ir.nbr_arguments, ir.lvalue, ir.type_call)
1547                internalcall.set_expression(ir.expression)
1548                internalcall.set_node(ir.node)
1549                internalcall.arguments = [ir.destination] + ir.arguments
1550                return_type = internalcall.function.return_type
1551                if return_type:
1552                    if len(return_type) == 1:
1553                        internalcall.lvalue.set_type(return_type[0])
1554                    elif len(return_type) > 1:
1555                        internalcall.lvalue.set_type(return_type)
1556                else:
1557                    internalcall.lvalue = None
1558                return internalcall
1559
1560        lib_contract = None
1561        if isinstance(destination, FunctionContract) and destination.contract.is_library:
1562            lib_contract = destination.contract
1563        elif isinstance(destination, UserDefinedType) and isinstance(destination.type, Contract):
1564            lib_contract = destination.type
1565
1566        if lib_contract:
1567            lib_call = LibraryCall(
1568                lib_contract,
1569                ir.function_name,
1570                ir.nbr_arguments,
1571                ir.lvalue,
1572                ir.type_call,
1573                names=ir.names,
1574            )
1575            lib_call.set_expression(ir.expression)
1576            lib_call.set_node(ir.node)
1577            lib_call.call_gas = ir.call_gas
1578            lib_call.arguments = [ir.destination] + ir.arguments
1579            new_ir = convert_type_library_call(lib_call, lib_contract)
1580            if new_ir:
1581                new_ir.set_node(ir.node)
1582                return new_ir
1583    return None
1584
1585
1586def convert_to_library_or_top_level(
1587    ir: HighLevelCall, node: "Node", using_for
1588) -> Optional[Union[LibraryCall, InternalCall,]]:
1589    t = ir.destination.type
1590    if t in using_for:
1591        new_ir = look_for_library_or_top_level(ir, using_for, t)
1592        if new_ir:
1593            return new_ir
1594
1595    if "*" in using_for:
1596        new_ir = look_for_library_or_top_level(ir, using_for, "*")
1597        if new_ir:
1598            return new_ir
1599
1600    if (
1601        isinstance(t, ElementaryType)
1602        and t.name == "address"
1603        and ir.destination.name == "this"
1604        and UserDefinedType(node.function.contract) in using_for
1605    ):
1606        new_ir = look_for_library_or_top_level(
1607            ir, using_for, UserDefinedType(node.function.contract)
1608        )
1609        if new_ir:
1610            return new_ir
1611
1612    return None
1613
1614
1615def get_type(
1616    t: Union[
1617        UserDefinedType,
1618        ElementaryType,
1619    ]
1620) -> str:
1621    """
1622    Convert a type to a str
1623    If the instance is a Contract, return 'address' instead
1624    """
1625    if isinstance(t, UserDefinedType):
1626        if isinstance(t.type, Contract):
1627            return "address"
1628    return str(t)
1629
1630
1631def _can_be_implicitly_converted(source: str, target: str) -> bool:
1632    if source in ElementaryTypeInt and target in ElementaryTypeInt:
1633        return int(source[3:]) <= int(target[3:])
1634    if source in Uint and target in Uint:
1635        return int(source[4:]) <= int(target[4:])
1636    return source == target
1637
1638
1639def convert_type_library_call(ir: HighLevelCall, lib_contract: Contract) -> Optional[LibraryCall]:
1640    func = None
1641    candidates = [
1642        f
1643        for f in lib_contract.functions
1644        if f.name == ir.function_name
1645        and not f.is_shadowed
1646        and len(f.parameters) == len(ir.arguments)
1647    ]
1648
1649    if len(candidates) == 1:
1650        func = candidates[0]
1651    # We can discard if there are arguments here because libraries only support constant variables
1652    # And constant variables cannot have non-value type
1653    # i.e. "uint[2] constant arr = [1,2];" is not possible in Solidity
1654    # If this were to change, the following condition might be broken
1655    if func is None and not ir.arguments:
1656        # TODO: handle collision with multiple state variables/functions
1657        func = lib_contract.get_state_variable_from_name(ir.function_name)
1658    if func is None and candidates:
1659        func = _find_function_from_parameter(ir.arguments, candidates, False)
1660
1661    # In case of multiple binding to the same type
1662    # TODO: this part might not be needed with _find_function_from_parameter
1663    if not func:
1664        # specific lookup when the compiler does implicit conversion
1665        # for example
1666        # myFunc(uint)
1667        # can be called with an uint8
1668        for function in lib_contract.functions:
1669            if function.name == ir.function_name and len(function.parameters) == len(ir.arguments):
1670                func = function
1671                break
1672    if not func:
1673        return None
1674    ir.function = func
1675    if isinstance(func, Function):
1676        t = func.return_type
1677        # if its not a tuple, return a singleton
1678        if t and len(t) == 1:
1679            t = t[0]
1680    else:
1681        # otherwise its a variable (getter)
1682        t = func.type
1683    if t:
1684        ir.lvalue.set_type(t)
1685    else:
1686        ir.lvalue = None
1687    return ir
1688
1689
1690def _convert_to_structure_to_list(return_type: Type) -> List[Type]:
1691    """
1692    Convert structure elements types to a list of types
1693    Recursive function
1694
1695    :param return_type:
1696    :return:
1697    """
1698    if isinstance(return_type, UserDefinedType) and isinstance(return_type.type, Structure):
1699        ret = []
1700        for v in return_type.type.elems_ordered:
1701            ret += _convert_to_structure_to_list(v.type)
1702        return ret
1703    # Mapping and arrays are not included in external call
1704    #
1705    # contract A{
1706    #
1707    #     struct St{
1708    #         uint a;
1709    #         uint b;
1710    #         mapping(uint => uint) map;
1711    #         uint[] array;
1712    #     }
1713    #
1714    #     mapping (uint => St) public st;
1715    #
1716    # }
1717    #
1718    # contract B{
1719    #
1720    #     function f(A a) public{
1721    #         (uint a, uint b) = a.st(0);
1722    #     }
1723    # }
1724    if isinstance(return_type, (MappingType, ArrayType)):
1725        return []
1726
1727    assert isinstance(return_type, (ElementaryType, UserDefinedType, TypeAlias))
1728    return [return_type]
1729
1730
1731def convert_type_of_high_and_internal_level_call(
1732    ir: Operation, contract: Optional[Contract]
1733) -> Optional[Operation]:
1734    """
1735    Convert the IR type based on heuristic
1736
1737    Args:
1738        ir: target
1739        contract: optional contract. This should be the target of the IR. It will be used to look up potential functions
1740
1741    Returns:
1742        Potential new IR
1743    """
1744
1745    func = None
1746    if isinstance(ir, InternalCall):
1747        candidates: List[Function]
1748        if ir.function_candidates:
1749            # This path is taken only for SolidityImportPlaceHolder
1750            # Here we have already done a filtering on the potential targets
1751            candidates = ir.function_candidates
1752        else:
1753            candidates = [
1754                f
1755                for f in contract.functions
1756                if f.name == ir.function_name
1757                and f.contract_declarer.name == ir.contract_name
1758                and len(f.parameters) == len(ir.arguments)
1759            ]
1760
1761            for import_statement in contract.file_scope.imports:
1762                if (
1763                    import_statement.alias is not None
1764                    and import_statement.alias == ir.contract_name
1765                ):
1766                    imported_scope = contract.compilation_unit.get_scope(import_statement.filename)
1767                    candidates += [
1768                        f
1769                        for f in list(imported_scope.functions)
1770                        if f.name == ir.function_name and len(f.parameters) == len(ir.arguments)
1771                    ]
1772
1773        func = _find_function_from_parameter(ir.arguments, candidates, False)
1774
1775        if not func:
1776            assert contract
1777            func = contract.get_state_variable_from_name(ir.function_name)
1778    else:
1779        assert isinstance(ir, HighLevelCall)
1780        assert contract
1781        candidates = [
1782            f
1783            for f in contract.functions
1784            if f.name == ir.function_name
1785            and not f.is_shadowed
1786            and len(f.parameters) == len(ir.arguments)
1787        ]
1788        if len(candidates) == 1:
1789            func = candidates[0]
1790        if func is None:
1791            # TODO: handle collision with multiple state variables/functions
1792            func = contract.get_state_variable_from_name(ir.function_name)
1793        if func is None and candidates:
1794            func = _find_function_from_parameter(ir.arguments, candidates, False)
1795
1796    # low level lookup needs to be done as last step
1797    if not func:
1798        if can_be_low_level(ir):
1799            return convert_to_low_level(ir)
1800        if can_be_solidity_func(ir):
1801            return convert_to_solidity_func(ir)
1802    if not func:
1803        to_log = f"Function not found {ir.function_name}"
1804        logger.error(to_log)
1805    ir.function = func
1806    if isinstance(func, Function):
1807        return_type = func.return_type
1808        # if its not a tuple; return a singleton
1809        if return_type and len(return_type) == 1:
1810            return_type = return_type[0]
1811    else:
1812        # otherwise its a variable (getter)
1813        # If its a mapping or a array
1814        # we iterate until we find the final type
1815        # mapping and array can be mixed together
1816        # ex:
1817        #    mapping ( uint => mapping ( uint => uint)) my_var
1818        #    mapping(uint => uint)[] test;p
1819        if isinstance(func.type, (MappingType, ArrayType)):
1820            tmp = func.type
1821            while isinstance(tmp, (MappingType, ArrayType)):
1822                if isinstance(tmp, MappingType):
1823                    tmp = tmp.type_to
1824                else:
1825                    tmp = tmp.type
1826            return_type = tmp
1827        else:
1828            return_type = func.type
1829    if return_type:
1830
1831        # If the return type is a structure, but the lvalue is a tuple
1832        # We convert the type of the structure to a list of element
1833        # TODO: explore to replace all tuple variables by structures
1834        if (
1835            isinstance(ir.lvalue, TupleVariable)
1836            and isinstance(return_type, UserDefinedType)
1837            and isinstance(return_type.type, Structure)
1838        ):
1839            return_type = _convert_to_structure_to_list(return_type)
1840
1841        ir.lvalue.set_type(return_type)
1842    else:
1843        ir.lvalue = None
1844
1845    return None
1846
1847
1848# endregion
1849###################################################################################
1850###################################################################################
1851# region Points to operation
1852###################################################################################
1853###################################################################################
1854
1855
1856def find_references_origin(irs: List[Operation]) -> None:
1857    """
1858    Make lvalue of each Index, Member operation
1859    points to the left variable
1860    """
1861    for ir in irs:
1862        if isinstance(ir, (Index, Member)):
1863            ir.lvalue.points_to = ir.variable_left
1864
1865
1866# endregion
1867###################################################################################
1868###################################################################################
1869# region Operation filtering
1870###################################################################################
1871###################################################################################
1872
1873
1874def remove_temporary(result):
1875    result = [
1876        ins
1877        for ins in result
1878        if not isinstance(
1879            ins,
1880            (
1881                Argument,
1882                TmpNewElementaryType,
1883                TmpNewContract,
1884                TmpNewArray,
1885                TmpNewStructure,
1886            ),
1887        )
1888    ]
1889
1890    return result
1891
1892
1893def remove_unused(result: List[Operation]) -> List[Operation]:
1894    removed = True
1895
1896    if not result:
1897        return result
1898
1899    # dont remove the last elem, as it may be used by RETURN
1900    last_elem = result[-1]
1901
1902    while removed:
1903        removed = False
1904
1905        to_keep = []
1906        to_remove = []
1907
1908        # keep variables that are read
1909        # and reference that are written
1910        for ins in result:
1911            to_keep += [str(x) for x in ins.read]
1912            if isinstance(ins, OperationWithLValue) and not isinstance(ins, (Index, Member)):
1913                if isinstance(ins.lvalue, ReferenceVariable):
1914                    to_keep += [str(ins.lvalue)]
1915
1916        for ins in result:
1917            if isinstance(ins, Member):
1918                if not ins.lvalue.name in to_keep and ins != last_elem:
1919                    to_remove.append(ins)
1920                    removed = True
1921            # Remove type(X) if X is an elementary type
1922            # This assume that type(X) is only used with min/max
1923            # If Solidity introduces other operation, we might remove this removal
1924            if isinstance(ins, SolidityCall) and ins.function == SolidityFunction("type()"):
1925                if isinstance(ins.arguments[0], ElementaryType):
1926                    to_remove.append(ins)
1927
1928        result = [i for i in result if not i in to_remove]
1929    return result
1930
1931
1932# endregion
1933###################################################################################
1934###################################################################################
1935# region Constant type conversion
1936###################################################################################
1937###################################################################################
1938
1939
1940def convert_constant_types(irs: List[Operation]) -> None:
1941    """
1942    late conversion of uint -> type for constant (Literal)
1943    :param irs:
1944    :return:
1945    """
1946    # TODO: implement instances lookup for events, NewContract
1947    was_changed = True
1948    while was_changed:
1949        was_changed = False
1950        for ir in irs:
1951            if isinstance(ir, (Assignment, Binary)):
1952                if (
1953                    isinstance(ir.lvalue.type, ElementaryType)
1954                    and ir.lvalue.type.type in ElementaryTypeInt
1955                ):
1956                    for r in ir.read:
1957                        if isinstance(r, Constant) and r.type.type not in ElementaryTypeInt:
1958                            r.set_type(ElementaryType(ir.lvalue.type.type))
1959                            was_changed = True
1960
1961            if isinstance(ir, (HighLevelCall, InternalCall)):
1962                func = ir.function
1963                if isinstance(func, StateVariable):
1964                    types = export_nested_types_from_variable(func)
1965                else:
1966                    types = [p.type for p in func.parameters]
1967                assert len(types) == len(ir.arguments)
1968                for idx, arg in enumerate(ir.arguments):
1969                    t = types[idx]
1970                    if isinstance(t, ElementaryType):
1971                        if t.type in ElementaryTypeInt:
1972                            if arg.type.type not in ElementaryTypeInt:
1973                                arg.set_type(ElementaryType(t.type))
1974                                was_changed = True
1975
1976            if isinstance(ir, NewStructure):
1977                st = ir.structure
1978                for idx, arg in enumerate(ir.arguments):
1979                    e = st.elems_ordered[idx]
1980                    if isinstance(e.type, ElementaryType):
1981                        if e.type.type in ElementaryTypeInt:
1982                            if arg.type.type not in ElementaryTypeInt:
1983                                arg.set_type(ElementaryType(e.type.type))
1984                                was_changed = True
1985
1986            def is_elementary_array(t):
1987                return isinstance(t, ArrayType) and isinstance(t.type, ElementaryType)
1988
1989            if isinstance(ir, InitArray):
1990                if is_elementary_array(ir.lvalue.type):
1991                    if ir.lvalue.type.type.type in ElementaryTypeInt:
1992                        for r in ir.read:
1993                            if isinstance(r, Constant) and is_elementary_array(r.type):
1994                                if r.type.type.type not in ElementaryTypeInt:
1995                                    r.set_type(ElementaryType(ir.lvalue.type.type.type))
1996                                    was_changed = True
1997
1998
1999# endregion
2000###################################################################################
2001###################################################################################
2002# region Delete handling
2003###################################################################################
2004###################################################################################
2005
2006
2007def convert_delete(irs: List[Operation]) -> None:
2008    """
2009    Convert the lvalue of the Delete to point to the variable removed
2010    This can only be done after find_references_origin is called
2011    :param irs:
2012    :return:
2013    """
2014    for ir in irs:
2015        if isinstance(ir, Delete):
2016            if isinstance(ir.lvalue, ReferenceVariable):
2017                ir.lvalue = ir.lvalue.points_to
2018
2019
2020# endregion
2021###################################################################################
2022###################################################################################
2023# region Source Mapping
2024###################################################################################
2025###################################################################################
2026
2027
2028def _find_source_mapping_references(irs: List[Operation]) -> None:
2029    for ir in irs:
2030
2031        if isinstance(ir, NewContract):
2032            ir.contract_created.references.append(ir.expression.source_mapping)
2033
2034        if isinstance(ir, HighLevelCall):
2035            ir.function.references.append(ir.expression.source_mapping)
2036
2037
2038# endregion
2039###################################################################################
2040###################################################################################
2041# region Heuristics selection
2042###################################################################################
2043###################################################################################
2044
2045
2046def apply_ir_heuristics(irs: List[Operation], node: "Node", is_solidity: bool) -> List[Operation]:
2047    """
2048    Apply a set of heuristic to improve slithIR
2049    """
2050
2051    irs = integrate_value_gas(irs)
2052
2053    irs = propagate_type_and_convert_call(irs, node)
2054    irs = remove_unused(irs)
2055    find_references_origin(irs)
2056
2057    # These are heuristics that are only applied to Solidity
2058    if is_solidity:
2059        convert_constant_types(irs)
2060        convert_delete(irs)
2061
2062    _find_source_mapping_references(irs)
2063
2064    return irs
logger = <Logger ConvertToIR (WARNING)>
 95def convert_expression(expression: Expression, node: "Node") -> List[Operation]:
 96    # handle standlone expression
 97    # such as return true;
 98    from slither.core.cfg.node import NodeType
 99
100    if isinstance(expression, Literal) and node.type in [NodeType.IF, NodeType.IFLOOP]:
101        cst = Constant(expression.value, expression.type)
102        cond = Condition(cst)
103        cond.set_expression(expression)
104        cond.set_node(node)
105        return [cond]
106    if isinstance(expression, Identifier) and node.type in [
107        NodeType.IF,
108        NodeType.IFLOOP,
109    ]:
110        cond = Condition(expression.value)
111        cond.set_expression(expression)
112        cond.set_node(node)
113        return [cond]
114
115    visitor = ExpressionToSlithIR(expression, node)
116    result = visitor.result()
117    is_solidity = node.compilation_unit.is_solidity
118    result = apply_ir_heuristics(result, node, is_solidity)
119
120    if result:
121        if node.type in [NodeType.IF, NodeType.IFLOOP]:
122            prev = result[-1]
123            assert isinstance(prev, (OperationWithLValue)) and prev.lvalue
124            cond = Condition(prev.lvalue)
125            cond.set_expression(expression)
126            cond.set_node(node)
127            result.append(cond)
128        elif node.type == NodeType.RETURN:
129            # May return None
130            prev = result[-1]
131            if isinstance(prev, (OperationWithLValue)):
132                r = Return(prev.lvalue)
133                r.set_expression(expression)
134                r.set_node(node)
135                result.append(r)
136
137    return result
def is_value(ins: slither.slithir.operations.operation.Operation) -> bool:
147def is_value(ins: Operation) -> bool:
148    if isinstance(ins, TmpCall):
149        if isinstance(ins.ori, Member):
150            if ins.ori.variable_right == "value":
151                return True
152    return False
def is_gas(ins: slither.slithir.operations.operation.Operation) -> bool:
155def is_gas(ins: Operation) -> bool:
156    if isinstance(ins, TmpCall):
157        if isinstance(ins.ori, Member):
158            if ins.ori.variable_right == "gas":
159                return True
160    return False
def is_temporary(ins: slither.slithir.operations.operation.Operation) -> bool:
275def is_temporary(ins: Operation) -> bool:
276    return isinstance(
277        ins,
278        (Argument, TmpNewElementaryType, TmpNewContract, TmpNewArray, TmpNewStructure),
279    )
def integrate_value_gas( result: List[slither.slithir.operations.operation.Operation]) -> List[slither.slithir.operations.operation.Operation]:
290def integrate_value_gas(result: List[Operation]) -> List[Operation]:
291    """
292    Integrate value and gas temporary arguments to call instruction
293    """
294    was_changed = True
295
296    calls = []
297
298    while was_changed:
299        # We loop until we do not find any call to value or gas
300        was_changed = False
301
302        # Find all the assignments
303        assigments = {}
304        for i in result:
305            if isinstance(i, OperationWithLValue) and i.lvalue:
306                assigments[i.lvalue.name] = i
307            if isinstance(i, TmpCall):
308                if isinstance(i.called, Variable) and i.called.name in assigments:
309                    ins_ori = assigments[i.called.name]
310                    i.set_ori(ins_ori)
311
312        to_remove = []
313        variable_to_replace = {}
314
315        # Replace call to value, gas to an argument of the real call
316        for idx, ins in enumerate(result):
317            # value can be shadowed, so we check that the prev ins
318            # is an Argument
319            if idx == 0:
320                continue
321            prev_ins = result[idx - 1]
322            if is_value(ins) and isinstance(prev_ins, Argument):
323                was_changed = True
324                prev_ins.set_type(ArgumentType.VALUE)
325                # Types checked by is_value
326                prev_ins.call_id = ins.ori.variable_left.name  # type: ignore
327                calls.append(ins.ori.variable_left)  # type: ignore
328                to_remove.append(ins)
329                variable_to_replace[ins.lvalue.name] = ins.ori.variable_left  # type: ignore
330            elif is_gas(ins) and isinstance(prev_ins, Argument):
331                was_changed = True
332                prev_ins.set_type(ArgumentType.GAS)
333                # Types checked by is_gas
334                prev_ins.call_id = ins.ori.variable_left.name  # type: ignore
335                calls.append(ins.ori.variable_left)  # type: ignore
336                to_remove.append(ins)
337                variable_to_replace[ins.lvalue.name] = ins.ori.variable_left  # type: ignore
338
339        # Remove the call to value/gas instruction
340        result = [i for i in result if not i in to_remove]
341
342        # update the real call
343        for ins in result:
344            if isinstance(ins, TmpCall):
345                # use of while if there redirections
346                while ins.called.name in variable_to_replace:
347                    was_changed = True
348                    ins.call_id = variable_to_replace[ins.called.name].name
349                    calls.append(ins.called)
350                    ins.called = variable_to_replace[ins.called.name]
351            if isinstance(ins, Argument):
352                while ins.call_id in variable_to_replace:
353                    was_changed = True
354                    ins.call_id = variable_to_replace[ins.call_id].name
355
356    calls = list({str(c) for c in calls})
357    idx = 0
358    calls_d = {}
359    for call in calls:
360        calls_d[str(call)] = idx
361        idx = idx + 1
362
363    return result

Integrate value and gas temporary arguments to call instruction

374def get_declared_param_names(
375    ins: Union[
376        NewStructure,
377        NewContract,
378        InternalCall,
379        LibraryCall,
380        HighLevelCall,
381        InternalDynamicCall,
382        EventCall,
383    ]
384) -> Optional[List[List[str]]]:
385    """
386    Given a call operation, return the list of parameter names, in the order
387    listed in the function declaration.
388    #### Parameters
389    ins -
390        The call instruction
391    #### Possible Returns
392    List[List[str]] -
393        A list of list of parameters in declaration order. Note only if it's a Function there can be multiple list
394    None -
395        Workaround: Unable to obtain list of parameters in declaration order
396    """
397    if isinstance(ins, NewStructure):
398        return [
399            [x.name for x in ins.structure.elems_ordered if not isinstance(x.type, MappingType)]
400        ]
401    if isinstance(ins, (InternalCall, LibraryCall, HighLevelCall)):
402        if isinstance(ins.function, Function):
403            res = [[p.name for p in ins.function.parameters]]
404            for f in ins.function.overrides:
405                res.append([p.name for p in f.parameters])
406            return res
407        return None
408    if isinstance(ins, InternalDynamicCall):
409        return [[p.name for p in ins.function_type.params]]
410
411    assert isinstance(ins, (EventCall, NewContract))
412    return None

Given a call operation, return the list of parameter names, in the order listed in the function declaration.

Parameters

ins - The call instruction

Possible Returns

List[List[str]] - A list of list of parameters in declaration order. Note only if it's a Function there can be multiple list None - Workaround: Unable to obtain list of parameters in declaration order

def reorder_arguments( args: List[slither.core.variables.variable.Variable], call_names: List[str], decl_names: List[List[str]]) -> List[slither.core.variables.variable.Variable]:
415def reorder_arguments(
416    args: List[Variable], call_names: List[str], decl_names: List[List[str]]
417) -> List[Variable]:
418    """
419    Reorder named struct constructor arguments so that they match struct declaration ordering rather
420    than call ordering
421    E.g. for `struct S { int x; int y; }` we reorder `S({y : 2, x : 3})` to `S(3, 2)`
422    #### Parameters
423    args -
424        Arguments to constructor call, in call order
425    names -
426        Parameter names in call order
427    decl_names -
428        List of list of parameter names in declaration order
429    #### Returns
430    Reordered arguments to constructor call, now in declaration order
431    """
432    assert len(args) == len(call_names)
433    for names in decl_names:
434        assert len(call_names) == len(names)
435
436    args_ret = []
437    index_seen = []  # Will have the correct index order
438
439    # We search for declaration of parameters that is fully compatible with the call arguments
440    # that is why if a arg name is not present in the call name we break and clear index_seen
441    # Known issue if the overridden function reuse the same parameters' name but in different positions
442    for names in decl_names:
443        if len(index_seen) == len(args):
444            break
445
446        for n in names:
447            try:
448                ind = call_names.index(n)
449                if ind in index_seen:
450                    continue
451            except ValueError:
452                index_seen.clear()
453                break
454            index_seen.append(ind)
455
456    for ind in index_seen:
457        args_ret.append(args[ind])
458
459    return args_ret

Reorder named struct constructor arguments so that they match struct declaration ordering rather than call ordering E.g. for struct S { int x; int y; } we reorder S({y : 2, x : 3}) to S(3, 2)

Parameters

args - Arguments to constructor call, in call order names - Parameter names in call order decl_names - List of list of parameter names in declaration order

Returns

Reordered arguments to constructor call, now in declaration order

def propagate_type_and_convert_call( result: List[slither.slithir.operations.operation.Operation], node: slither.core.cfg.node.Node) -> List[slither.slithir.operations.operation.Operation]:
462def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> List[Operation]:
463    """
464    Propagate the types variables and convert tmp call to real call operation
465    """
466    calls_value = {}
467    calls_gas = {}
468
469    call_data = []
470
471    idx = 0
472    # use of while len() as result can be modified during the iteration
473    while idx < len(result):
474        ins = result[idx]
475
476        if isinstance(ins, TmpCall):
477            # If the node.function is a FunctionTopLevel, then it does not have a contract
478            contract = (
479                node.function.contract if isinstance(node.function, FunctionContract) else None
480            )
481            new_ins = extract_tmp_call(ins, contract)
482            if new_ins:
483                new_ins.set_node(ins.node)
484                ins = new_ins
485                result[idx] = ins
486
487        # If there's two consecutive type conversions, keep only the last
488        # ARG_CALL x                             call_data = [x]
489        # TMP_4 = CONVERT x to Fix               call_data = []
490        # TMP_5 = CONVERT TMP_4 to uint192       call_data = [TMP_5]
491        if isinstance(ins, TypeConversion) and ins.variable in call_data:
492            call_data.remove(ins.variable)
493
494        if isinstance(ins, Argument):
495            # In case of duplicate arguments we overwrite the value
496            # This can happen because of addr.call.value(1).value(2)
497            if ins.get_type() in [ArgumentType.GAS]:
498                calls_gas[ins.call_id] = ins.argument
499            elif ins.get_type() in [ArgumentType.VALUE]:
500                calls_value[ins.call_id] = ins.argument
501            else:
502                assert ins.get_type() == ArgumentType.CALL
503                call_data.append(ins.argument)
504
505        if isinstance(ins, (HighLevelCall, NewContract, InternalDynamicCall)):
506            if ins.call_id in calls_value:
507                ins.call_value = calls_value[ins.call_id]
508            if ins.call_id in calls_gas and isinstance(ins, (HighLevelCall, InternalDynamicCall)):
509                ins.call_gas = calls_gas[ins.call_id]
510
511        if isinstance(ins, Call) and (ins.names is not None):
512            assert isinstance(
513                ins,
514                (
515                    NewStructure,
516                    NewContract,
517                    InternalCall,
518                    LibraryCall,
519                    HighLevelCall,
520                    InternalDynamicCall,
521                    EventCall,
522                ),
523            )
524            decl_param_names = get_declared_param_names(ins)
525            if decl_param_names is not None:
526                call_data = reorder_arguments(call_data, ins.names, decl_param_names)
527
528        if isinstance(ins, (Call, NewContract, NewStructure)):
529            # We might have stored some arguments for libraries
530            if ins.arguments:
531                call_data = ins.arguments + call_data
532            ins.arguments = call_data
533            call_data = []
534
535        if is_temporary(ins):
536            del result[idx]
537            continue
538
539        new_ins = propagate_types(ins, node)
540        if new_ins:
541            if isinstance(new_ins, (list,)):
542                for new_ins_ in new_ins:
543                    new_ins_.set_node(ins.node)
544                del result[idx]
545                for i, ins in enumerate(new_ins):
546                    result.insert(idx + i, ins)
547                idx = idx + len(new_ins) - 1
548            else:
549                new_ins.set_node(ins.node)
550                result[idx] = new_ins
551        idx = idx + 1
552    return result

Propagate the types variables and convert tmp call to real call operation

def propagate_types( ir: slither.slithir.operations.operation.Operation, node: slither.core.cfg.node.Node):
604def propagate_types(ir: Operation, node: "Node"):  # pylint: disable=too-many-locals
605    # propagate the type
606    node_function = node.function
607
608    using_for: USING_FOR = {}
609    if isinstance(node_function, FunctionContract):
610        using_for = node_function.contract.using_for_complete
611    elif isinstance(node_function, FunctionTopLevel):
612        using_for = node_function.using_for_complete
613
614    if isinstance(ir, OperationWithLValue) and ir.lvalue:
615        # Force assignment in case of missing previous correct type
616        if not ir.lvalue.type:
617            if isinstance(ir, Assignment):
618                ir.lvalue.set_type(ir.rvalue.type)
619            elif isinstance(ir, Binary):
620                if BinaryType.return_bool(ir.type):
621                    ir.lvalue.set_type(ElementaryType("bool"))
622                else:
623                    ir.lvalue.set_type(ir.variable_left.type)
624            elif isinstance(ir, Delete):
625                # nothing to propagate
626                pass
627            elif isinstance(ir, LibraryCall):
628                return convert_type_library_call(ir, ir.destination)
629            elif isinstance(ir, HighLevelCall):
630                t = ir.destination.type
631                # Temporary operation (they are removed later)
632                if t is None:
633                    return None
634
635                if isinstance(t, ElementaryType) and t.name == "address":
636                    if can_be_solidity_func(ir):
637                        return convert_to_solidity_func(ir)
638
639                # convert library or top level function
640                if t in using_for or "*" in using_for:
641                    new_ir = convert_to_library_or_top_level(ir, node, using_for)
642                    if new_ir:
643                        return new_ir
644
645                # convert library function when used with "this"
646                if (
647                    isinstance(t, ElementaryType)
648                    and t.name == "address"
649                    and ir.destination.name == "this"
650                    and UserDefinedType(node_function.contract) in using_for
651                ):
652                    new_ir = convert_to_library_or_top_level(ir, node, using_for)
653                    if new_ir:
654                        return new_ir
655
656                if isinstance(t, UserDefinedType):
657                    # UserdefinedType
658                    t_type = t.type
659                    if isinstance(t_type, Contract):
660                        # the target contract of the IR is the t_type (the destination of the call)
661                        return convert_type_of_high_and_internal_level_call(ir, t_type)
662
663                # Convert HighLevelCall to LowLevelCall
664                if (isinstance(t, ElementaryType) and t.name == "address") or (
665                    isinstance(t, TypeAlias) and t.underlying_type.name == "address"
666                ):
667                    if ir.destination.name == "this":
668                        # Cannot be a top level function with this.
669                        assert isinstance(node_function, FunctionContract)
670                        # the target contract is the contract itself
671                        return convert_type_of_high_and_internal_level_call(
672                            ir, node_function.contract
673                        )
674                    if can_be_low_level(ir):
675                        return convert_to_low_level(ir)
676
677                # Convert push operations
678                # May need to insert a new operation
679                # Which leads to return a list of operation
680                if isinstance(t, ArrayType) or (
681                    isinstance(t, ElementaryType) and t.type == "bytes"
682                ):
683                    # Solidity uses push
684                    # Vyper uses append
685                    if ir.function_name in ["push", "append"] and len(ir.arguments) <= 1:
686                        return convert_to_push(ir, node)
687                    if ir.function_name == "pop" and len(ir.arguments) == 0:
688                        return convert_to_pop(ir, node)
689
690            elif isinstance(ir, Index):
691                if isinstance(ir.variable_left.type, MappingType):
692                    ir.lvalue.set_type(ir.variable_left.type.type_to)
693                elif isinstance(ir.variable_left.type, ArrayType):
694                    ir.lvalue.set_type(ir.variable_left.type.type)
695
696            elif isinstance(ir, InitArray):
697                length = len(ir.init_values)
698                t = ir.init_values[0].type
699                ir.lvalue.set_type(ArrayType(t, length))
700            elif isinstance(ir, InternalCall):
701                # if its not a tuple, return a singleton
702                if ir.function is None:
703                    func = node.function
704                    function_contract = (
705                        func.contract if isinstance(func, FunctionContract) else None
706                    )
707                    # the target contract might be None if its a top level function
708                    convert_type_of_high_and_internal_level_call(ir, function_contract)
709                return_type = ir.function.return_type
710                if return_type:
711                    if len(return_type) == 1:
712                        ir.lvalue.set_type(return_type[0])
713                    elif len(return_type) > 1:
714                        ir.lvalue.set_type(return_type)
715                else:
716                    ir.lvalue = None
717            elif isinstance(ir, InternalDynamicCall):
718                # if its not a tuple, return a singleton
719                return_type = ir.function_type.return_type
720                if return_type:
721                    if len(return_type) == 1:
722                        ir.lvalue.set_type(return_type[0])
723                    else:
724                        ir.lvalue.set_type(return_type)
725                else:
726                    ir.lvalue = None
727            elif isinstance(ir, LowLevelCall):
728                # Call are not yet converted
729                # This should not happen
730                assert False
731            elif isinstance(ir, Member):
732                # TODO we should convert the reference to a temporary if the member is a length or a balance
733                if (
734                    ir.variable_right == "length"
735                    and not isinstance(ir.variable_left, Contract)
736                    and isinstance(ir.variable_left.type, (ElementaryType, ArrayType))
737                ):
738                    new_length = Length(ir.variable_left, ir.lvalue)
739                    assert ir.expression
740                    new_length.set_expression(ir.expression)
741                    new_length.lvalue.points_to = ir.variable_left
742                    new_length.set_node(ir.node)
743                    return new_length
744                # This only happen for .balance/code/codehash access on a variable for which we dont know at
745                # early parsing time the type
746                # Like
747                # function return_addr() internal returns(addresss)
748                #
749                # return_addr().balance
750                # Here slithIR will incorrectly create a REF variable instead of a Temp variable
751                # However this pattern does not appear so often
752                if (
753                    ir.variable_right.name in ["balance", "code", "codehash"]
754                    and not isinstance(ir.variable_left, Contract)
755                    and isinstance(ir.variable_left.type, ElementaryType)
756                ):
757                    name = ir.variable_right.name + "(address)"
758                    sol_func = SolidityFunction(name)
759                    s = SolidityCall(
760                        sol_func,
761                        1,
762                        ir.lvalue,
763                        sol_func.return_type,
764                    )
765                    s.arguments.append(ir.variable_left)
766                    s.set_expression(ir.expression)
767                    s.lvalue.set_type(sol_func.return_type)
768                    s.set_node(ir.node)
769                    return s
770                if (
771                    ir.variable_right == "codesize"
772                    and not isinstance(ir.variable_left, Contract)
773                    and isinstance(ir.variable_left.type, ElementaryType)
774                ):
775                    b = CodeSize(ir.variable_left, ir.lvalue)
776                    b.set_expression(ir.expression)
777                    b.set_node(ir.node)
778                    return b
779                if ir.variable_right == "selector" and isinstance(ir.variable_left, (CustomError)):
780                    assignment = Assignment(
781                        ir.lvalue,
782                        Constant(
783                            str(get_function_id(ir.variable_left.solidity_signature)),
784                            ElementaryType("bytes4"),
785                        ),
786                        ElementaryType("bytes4"),
787                    )
788                    assignment.set_expression(ir.expression)
789                    assignment.set_node(ir.node)
790                    assignment.lvalue.set_type(ElementaryType("bytes4"))
791                    return assignment
792
793                if isinstance(ir.variable_right, (CustomError)):
794                    assignment = Assignment(
795                        ir.lvalue,
796                        Constant(
797                            str(get_function_id(ir.variable_left.solidity_signature)),
798                            ElementaryType("bytes4"),
799                        ),
800                        ElementaryType("bytes4"),
801                    )
802                    assignment.set_expression(ir.expression)
803                    assignment.set_node(ir.node)
804                    assignment.lvalue.set_type(ElementaryType("bytes4"))
805                    return assignment
806                if ir.variable_right == "selector" and isinstance(ir.variable_left, (Event)):
807                    # the event selector returns a bytes32, which is different from the error/function selector
808                    # which returns a bytes4
809                    assignment = Assignment(
810                        ir.lvalue,
811                        Constant(
812                            str(get_event_id(ir.variable_left.full_name)), ElementaryType("bytes32")
813                        ),
814                        ElementaryType("bytes32"),
815                    )
816                    assignment.set_expression(ir.expression)
817                    assignment.set_node(ir.node)
818                    assignment.lvalue.set_type(ElementaryType("bytes32"))
819                    return assignment
820                if ir.variable_right == "selector" and (
821                    isinstance(ir.variable_left.type, (Function))
822                ):
823                    assignment = Assignment(
824                        ir.lvalue,
825                        Constant(
826                            str(get_function_id(ir.variable_left.type.full_name)),
827                            ElementaryType("bytes4"),
828                        ),
829                        ElementaryType("bytes4"),
830                    )
831                    assignment.set_expression(ir.expression)
832                    assignment.set_node(ir.node)
833                    assignment.lvalue.set_type(ElementaryType("bytes4"))
834                    return assignment
835                if isinstance(ir.variable_left, TemporaryVariable) and isinstance(
836                    ir.variable_left.type, TypeInformation
837                ):
838                    return _convert_type_contract(ir)
839                left = ir.variable_left
840                t = None
841                ir_func = ir.node.function
842                # Handling of this.function_name usage
843                if (
844                    left == SolidityVariable("this")
845                    and isinstance(ir.variable_right, Constant)
846                    and isinstance(ir_func, FunctionContract)
847                    and str(ir.variable_right) in [x.name for x in ir_func.contract.functions]
848                ):
849                    # Assumption that this.function_name can only compile if
850                    # And the contract does not have two functions starting with function_name
851                    # Otherwise solc raises:
852                    # Error: Member "f" not unique after argument-dependent lookup in contract
853                    targeted_function = next(
854                        (x for x in ir_func.contract.functions if x.name == str(ir.variable_right))
855                    )
856                    ir.lvalue.set_type(targeted_function)
857                elif isinstance(left, (Variable, SolidityVariable)):
858                    t = left.type
859                elif isinstance(left, (Contract, Enum, Structure)):
860                    t = UserDefinedType(left)
861                # can be None due to temporary operation
862                if t:
863                    if isinstance(t, UserDefinedType):
864                        # UserdefinedType
865                        type_t = t.type
866                        if isinstance(type_t, Enum):
867                            ir.lvalue.set_type(t)
868                        elif isinstance(type_t, Structure):
869                            elems = type_t.elems
870                            for elem in elems:
871                                if elem == ir.variable_right:
872                                    ir.lvalue.set_type(elems[elem].type)
873                        else:
874                            assert isinstance(type_t, Contract)
875                            # Allow type propagation as a Function
876                            # Only for reference variables
877                            # This allows to track the selector keyword
878                            # We don't need to check for function collision, as solc prevents the use of selector
879                            # if there are multiple functions with the same name
880                            f = next(
881                                (f for f in type_t.functions if f.name == ir.variable_right),
882                                None,
883                            )
884                            if f:
885                                ir.lvalue.set_type(f)
886                            else:
887                                # Allow propagation for variable access through contract's name
888                                # like Base_contract.my_variable
889                                v = next(
890                                    (
891                                        v
892                                        for v in type_t.state_variables
893                                        if v.name == ir.variable_right
894                                    ),
895                                    None,
896                                )
897                                if v:
898                                    ir.lvalue.set_type(v.type)
899            elif isinstance(ir, NewArray):
900                ir.lvalue.set_type(ir.array_type)
901            elif isinstance(ir, NewContract):
902                ir.lvalue.set_type(ir.contract_name)
903            elif isinstance(ir, NewElementaryType):
904                ir.lvalue.set_type(ir.type)
905            elif isinstance(ir, NewStructure):
906                ir.lvalue.set_type(UserDefinedType(ir.structure))
907            elif isinstance(ir, Send):
908                ir.lvalue.set_type(ElementaryType("bool"))
909            elif isinstance(ir, SolidityCall):
910                if ir.function.name in ["type(address)", "type()"]:
911                    ir.function.return_type = [TypeInformation(ir.arguments[0])]
912                return_type = ir.function.return_type
913                if len(return_type) == 1:
914                    ir.lvalue.set_type(return_type[0])
915                elif len(return_type) > 1:
916                    ir.lvalue.set_type(return_type)
917            elif isinstance(ir, TypeConversion):
918                ir.lvalue.set_type(ir.type)
919            elif isinstance(ir, Unary):
920                ir.lvalue.set_type(ir.rvalue.type)
921            elif isinstance(ir, Unpack):
922                types = ir.tuple.type.type
923                idx = ir.index
924                t = types[idx]
925                ir.lvalue.set_type(t)
926            elif isinstance(
927                ir,
928                (
929                    Argument,
930                    TmpCall,
931                    TmpNewArray,
932                    TmpNewContract,
933                    TmpNewStructure,
934                    TmpNewElementaryType,
935                ),
936            ):
937                # temporary operation; they will be removed
938                pass
939            else:
940                raise SlithIRError(f"Not handling {type(ir)} during type propagation")
941    return None
 945def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, Nop]:
 946    assert isinstance(ins, TmpCall)
 947    if isinstance(ins.called, Variable) and isinstance(ins.called.type, FunctionType):
 948        # If the call is made to a variable member, where the member is this
 949        # We need to convert it to a HighLelelCall and not an internal dynamic call
 950        if isinstance(ins.ori, Member) and ins.ori.variable_left == SolidityVariable("this"):
 951            pass
 952        else:
 953            call = InternalDynamicCall(ins.lvalue, ins.called, ins.called.type)
 954            call.set_expression(ins.expression)
 955            call.call_id = ins.call_id
 956            return call
 957    if isinstance(ins.ori, Member):
 958        # If there is a call on an inherited contract, it is an internal call or an event
 959        if contract and ins.ori.variable_left in contract.inheritance + [contract]:
 960            if str(ins.ori.variable_right) in [f.name for f in contract.functions]:
 961                internalcall = InternalCall(
 962                    (ins.ori.variable_right, ins.ori.variable_left.name),
 963                    ins.nbr_arguments,
 964                    ins.lvalue,
 965                    ins.type_call,
 966                )
 967                internalcall.set_expression(ins.expression)
 968                internalcall.call_id = ins.call_id
 969                return internalcall
 970            if str(ins.ori.variable_right) in [f.name for f in contract.events]:
 971                eventcall = EventCall(ins.ori.variable_right)
 972                eventcall.set_expression(ins.expression)
 973                eventcall.call_id = ins.call_id
 974                return eventcall
 975        if isinstance(ins.ori.variable_left, Contract):
 976            st = ins.ori.variable_left.get_structure_from_name(ins.ori.variable_right)
 977            if st:
 978                op = NewStructure(st, ins.lvalue, names=ins.names)
 979                op.set_expression(ins.expression)
 980                op.call_id = ins.call_id
 981                return op
 982
 983            # For event emit through library
 984            # lib L { event E()}
 985            # ...
 986            # emit L.E();
 987            if str(ins.ori.variable_right) in [f.name for f in ins.ori.variable_left.events]:
 988                eventcall = EventCall(ins.ori.variable_right)
 989                eventcall.set_expression(ins.expression)
 990                eventcall.call_id = ins.call_id
 991                return eventcall
 992
 993            # lib Lib { error Error()} ... revert Lib.Error()
 994            if str(ins.ori.variable_right) in ins.ori.variable_left.custom_errors_as_dict:
 995                custom_error = ins.ori.variable_left.custom_errors_as_dict[
 996                    str(ins.ori.variable_right)
 997                ]
 998                assert isinstance(
 999                    custom_error,
1000                    CustomError,
1001                )
1002                sol_function = SolidityCustomRevert(custom_error)
1003                solidity_call = SolidityCall(
1004                    sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call
1005                )
1006                solidity_call.set_expression(ins.expression)
1007                return solidity_call
1008
1009            libcall = LibraryCall(
1010                ins.ori.variable_left,
1011                ins.ori.variable_right,
1012                ins.nbr_arguments,
1013                ins.lvalue,
1014                ins.type_call,
1015                names=ins.names,
1016            )
1017            libcall.set_expression(ins.expression)
1018            libcall.call_id = ins.call_id
1019            return libcall
1020        if isinstance(ins.ori.variable_left, Function):
1021            # Support for library call where the parameter is a function
1022            # We could merge this with the standard library handling
1023            # Except that we will have some troubles with using_for
1024            # As the type of the function will not match function()
1025            # Additionally we do not have a correct view on the parameters of the tmpcall
1026            # At this level
1027            #
1028            # library FunctionExtensions {
1029            #     function h(function() internal _t, uint8) internal {  }
1030            # }
1031            # contract FunctionMembers {
1032            #     using FunctionExtensions for function();
1033            #
1034            #     function f() public {
1035            #         f.h(1);
1036            #     }
1037            # }
1038            node_func = ins.node.function
1039            using_for = (
1040                node_func.contract.using_for_complete
1041                if isinstance(node_func, FunctionContract)
1042                else {}
1043            )
1044
1045            targeted_libraries = (
1046                [] + using_for.get("*", []) + using_for.get(FunctionType([], []), [])
1047            )
1048            lib_contract: Contract
1049            candidates = []
1050            for lib_contract_type in targeted_libraries:
1051                if not isinstance(lib_contract_type, UserDefinedType) and isinstance(
1052                    lib_contract_type.type, Contract
1053                ):
1054                    continue
1055                if isinstance(lib_contract_type, FunctionContract):
1056                    # Using for with list of functions, this is the function called
1057                    candidates.append(lib_contract_type)
1058                else:
1059                    lib_contract = lib_contract_type.type
1060                    for lib_func in lib_contract.functions:
1061                        if lib_func.name == ins.ori.variable_right:
1062                            candidates.append(lib_func)
1063
1064            if len(candidates) == 1:
1065                lib_func = candidates[0]
1066                # Library must be from a contract
1067                assert isinstance(lib_func, FunctionContract)
1068                lib_call = LibraryCall(
1069                    lib_func.contract,
1070                    Constant(lib_func.name),
1071                    len(lib_func.parameters),
1072                    ins.lvalue,
1073                    "d",
1074                    names=ins.names,
1075                )
1076                lib_call.set_expression(ins.expression)
1077                lib_call.set_node(ins.node)
1078                lib_call.call_gas = ins.call_gas
1079                lib_call.call_id = ins.call_id
1080                lib_call.set_node(ins.node)
1081                lib_call.function = lib_func
1082                lib_call.arguments.append(ins.ori.variable_left)
1083                return lib_call
1084            # We do not support something lik
1085            # library FunctionExtensions {
1086            #     function h(function() internal _t, uint8) internal {  }
1087            #     function h(function() internal _t, bool) internal {  }
1088            # }
1089            # contract FunctionMembers {
1090            #     using FunctionExtensions for function();
1091            #
1092            #     function f() public {
1093            #         f.h(1);
1094            #     }
1095            # }
1096            to_log = "Slither does not support dynamic functions to libraries if functions have the same name"
1097            to_log += f"{[candidate.full_name for candidate in candidates]}"
1098            raise SlithIRError(to_log)
1099        if isinstance(ins.ori.variable_left, SolidityImportPlaceHolder):
1100            # For top level import, where the import statement renames the filename
1101            # See https://blog.soliditylang.org/2020/09/02/solidity-0.7.1-release-announcement/
1102
1103            current_path = Path(ins.ori.variable_left.source_mapping.filename.absolute).parent
1104            target = str(
1105                Path(current_path, ins.ori.variable_left.import_directive.filename).absolute()
1106            )
1107            top_level_function_targets = [
1108                f
1109                for f in ins.compilation_unit.functions_top_level
1110                if f.source_mapping.filename.absolute == target
1111                and f.name == ins.ori.variable_right
1112                and len(f.parameters) == ins.nbr_arguments
1113            ]
1114
1115            internalcall = InternalCall(
1116                (ins.ori.variable_right, ins.ori.variable_left.name),
1117                ins.nbr_arguments,
1118                ins.lvalue,
1119                ins.type_call,
1120            )
1121            internalcall.set_expression(ins.expression)
1122            internalcall.call_id = ins.call_id
1123            internalcall.function_candidates = top_level_function_targets
1124            return internalcall
1125
1126        if ins.ori.variable_left == ElementaryType("bytes") and ins.ori.variable_right == Constant(
1127            "concat"
1128        ):
1129            s = SolidityCall(
1130                SolidityFunction("bytes.concat()"),
1131                ins.nbr_arguments,
1132                ins.lvalue,
1133                ins.type_call,
1134            )
1135            s.set_expression(ins.expression)
1136            return s
1137
1138        if ins.ori.variable_left == ElementaryType("string") and ins.ori.variable_right == Constant(
1139            "concat"
1140        ):
1141            s = SolidityCall(
1142                SolidityFunction("string.concat()"),
1143                ins.nbr_arguments,
1144                ins.lvalue,
1145                ins.type_call,
1146            )
1147            s.set_expression(ins.expression)
1148            return s
1149
1150        msgcall = HighLevelCall(
1151            ins.ori.variable_left,
1152            ins.ori.variable_right,
1153            ins.nbr_arguments,
1154            ins.lvalue,
1155            ins.type_call,
1156            names=ins.names,
1157        )
1158        msgcall.call_id = ins.call_id
1159
1160        if ins.call_gas:
1161            msgcall.call_gas = ins.call_gas
1162        if ins.call_value:
1163            msgcall.call_value = ins.call_value
1164        msgcall.set_expression(ins.expression)
1165
1166        return msgcall
1167
1168    if isinstance(ins.ori, TmpCall):
1169        r = extract_tmp_call(ins.ori, contract)
1170        r.set_node(ins.node)
1171        return r
1172    if isinstance(ins.called, SolidityVariableComposed):
1173        if str(ins.called) == "block.blockhash":
1174            ins.called = SolidityFunction("blockhash(uint256)")
1175
1176    if isinstance(ins.called, SolidityFunction):
1177        s = SolidityCall(ins.called, ins.nbr_arguments, ins.lvalue, ins.type_call)
1178        s.set_expression(ins.expression)
1179        return s
1180
1181    if isinstance(ins.called, CustomError):
1182        sol_function = SolidityCustomRevert(ins.called)
1183        s = SolidityCall(sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call)
1184        s.set_expression(ins.expression)
1185        return s
1186
1187    if isinstance(ins.ori, TmpNewElementaryType):
1188        n = NewElementaryType(ins.ori.type, ins.lvalue)
1189        n.set_expression(ins.expression)
1190        return n
1191
1192    if isinstance(ins.ori, TmpNewContract):
1193        op = NewContract(ins.ori.contract_name, ins.lvalue)
1194        op.set_expression(ins.expression)
1195        op.call_id = ins.call_id
1196        if ins.call_value:
1197            op.call_value = ins.call_value
1198        if ins.call_salt:
1199            op.call_salt = ins.call_salt
1200        return op
1201
1202    if isinstance(ins.ori, TmpNewArray):
1203        n = NewArray(ins.ori.array_type, ins.lvalue)
1204        n.set_expression(ins.expression)
1205        return n
1206
1207    if isinstance(ins.called, Structure):
1208        op = NewStructure(ins.called, ins.lvalue, names=ins.names)
1209        op.set_expression(ins.expression)
1210        op.call_id = ins.call_id
1211        op.set_expression(ins.expression)
1212        return op
1213
1214    if isinstance(ins.called, Event):
1215        e = EventCall(ins.called.name)
1216        e.set_expression(ins.expression)
1217        return e
1218
1219    if isinstance(ins.called, Contract):
1220        # Called a base constructor, where there is no constructor
1221        if ins.called.constructor is None:
1222            return Nop()
1223        # Case where:
1224        # contract A{ constructor(uint) }
1225        # contract B is A {}
1226        # contract C is B{ constructor() A(10) B() {}
1227        # C calls B(), which does not exist
1228        # Ideally we should compare here for the parameters types too
1229        if len(ins.called.constructor.parameters) != ins.nbr_arguments:
1230            return Nop()
1231        internalcall = InternalCall(
1232            ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call, ins.names
1233        )
1234        internalcall.call_id = ins.call_id
1235        internalcall.set_expression(ins.expression)
1236        return internalcall
1237
1238    raise SlithIRError(f"Not extracted {type(ins.called)} {ins}")
def can_be_low_level(ir: slither.slithir.operations.high_level_call.HighLevelCall) -> bool:
1249def can_be_low_level(ir: HighLevelCall) -> bool:
1250    return ir.function_name in [
1251        "transfer",
1252        "send",
1253        "call",
1254        "delegatecall",
1255        "callcode",
1256        "staticcall",
1257        "raw_call",
1258    ]
1261def convert_to_low_level(
1262    ir: HighLevelCall,
1263) -> Union[Send, LowLevelCall, Transfer,]:
1264    """
1265    Convert to a transfer/send/or low level call
1266    The function assume to receive a correct IR
1267    The checks must be done by the caller
1268
1269    Must be called after can_be_low_level
1270    """
1271    if ir.function_name == "transfer":
1272        assert len(ir.arguments) == 1
1273        prev_ir = ir
1274        ir = Transfer(ir.destination, ir.arguments[0])
1275        ir.set_expression(prev_ir.expression)
1276        ir.set_node(prev_ir.node)
1277        return ir
1278    if ir.function_name == "send":
1279        assert len(ir.arguments) == 1
1280        prev_ir = ir
1281        ir = Send(ir.destination, ir.arguments[0], ir.lvalue)
1282        ir.set_expression(prev_ir.expression)
1283        ir.set_node(prev_ir.node)
1284        ir.lvalue.set_type(ElementaryType("bool"))
1285        return ir
1286    if ir.function_name in ["call", "delegatecall", "callcode", "staticcall", "raw_call"]:
1287        new_ir = LowLevelCall(
1288            ir.destination, ir.function_name, ir.nbr_arguments, ir.lvalue, ir.type_call
1289        )
1290        new_ir.call_gas = ir.call_gas
1291        new_ir.call_value = ir.call_value
1292        new_ir.arguments = ir.arguments
1293        # TODO fix this for Vyper
1294        if ir.node.compilation_unit.solc_version >= "0.5":
1295            new_ir.lvalue.set_type([ElementaryType("bool"), ElementaryType("bytes")])
1296        else:
1297            new_ir.lvalue.set_type(ElementaryType("bool"))
1298        new_ir.set_expression(ir.expression)
1299        new_ir.set_node(ir.node)
1300        return new_ir
1301    raise SlithIRError(f"Incorrect conversion to low level {ir}")

Convert to a transfer/send/or low level call The function assume to receive a correct IR The checks must be done by the caller

Must be called after can_be_low_level

def can_be_solidity_func(ir: slither.slithir.operations.high_level_call.HighLevelCall) -> bool:
1304def can_be_solidity_func(ir: HighLevelCall) -> bool:
1305    if not isinstance(ir, HighLevelCall):
1306        return False
1307    return ir.destination.name == "abi" and ir.function_name in [
1308        "encode",
1309        "encodePacked",
1310        "encodeWithSelector",
1311        "encodeWithSignature",
1312        "encodeCall",
1313        "decode",
1314    ]
1317def convert_to_solidity_func(
1318    ir: HighLevelCall,
1319) -> SolidityCall:
1320    """
1321    Must be called after can_be_solidity_func
1322    :param ir:
1323    :return:
1324    """
1325    call = SolidityFunction(f"abi.{ir.function_name}()")
1326    new_ir = SolidityCall(call, ir.nbr_arguments, ir.lvalue, ir.type_call)
1327    new_ir.arguments = ir.arguments
1328    new_ir.set_expression(ir.expression)
1329    new_ir.set_node(ir.node)
1330    if isinstance(call.return_type, list) and len(call.return_type) == 1:
1331        new_ir.lvalue.set_type(call.return_type[0])
1332    elif (
1333        isinstance(new_ir.lvalue, TupleVariable)
1334        and call == SolidityFunction("abi.decode()")
1335        and len(new_ir.arguments) == 2
1336        and isinstance(new_ir.arguments[1], list)
1337    ):
1338        types = []
1339        for arg_type in new_ir.arguments[1]:
1340            decode_type = arg_type
1341            if isinstance(decode_type, (Structure, Enum, Contract)):
1342                decode_type = UserDefinedType(decode_type)
1343            types.append(decode_type)
1344        new_ir.lvalue.set_type(types)
1345    # abi.decode where the type to decode is a singleton
1346    # abi.decode(a, (uint))
1347    elif call == SolidityFunction("abi.decode()") and len(new_ir.arguments) == 2:
1348        # If the variable is a referenceVariable, we are lost
1349        # See https://github.com/crytic/slither/issues/566 for potential solutions
1350        if not isinstance(new_ir.arguments[1], ReferenceVariable):
1351            decode_type = new_ir.arguments[1]
1352            if isinstance(decode_type, (Structure, Enum, Contract)):
1353                decode_type = UserDefinedType(decode_type)
1354            new_ir.lvalue.set_type(decode_type)
1355    else:
1356        new_ir.lvalue.set_type(call.return_type)
1357    return new_ir

Must be called after can_be_solidity_func

Parameters
  • ir:
Returns
1360def convert_to_push_expand_arr(
1361    ir: HighLevelCall, node: "Node", ret: List[Any]
1362) -> TemporaryVariable:
1363    arr = ir.destination
1364
1365    length = ReferenceVariable(node)
1366    length.set_type(ElementaryType("uint256"))
1367
1368    ir_length = Length(arr, length)
1369    ir_length.set_expression(ir.expression)
1370    ir_length.set_node(ir.node)
1371    ir_length.lvalue.points_to = arr
1372    ret.append(ir_length)
1373
1374    length_val = TemporaryVariable(node)
1375    length_val.set_type(ElementaryType("uint256"))
1376    ir_get_length = Assignment(length_val, length, ElementaryType("uint256"))
1377    ir_get_length.set_expression(ir.expression)
1378    ir_get_length.set_node(ir.node)
1379    ret.append(ir_get_length)
1380
1381    new_length_val = TemporaryVariable(node)
1382    ir_add_1 = Binary(
1383        new_length_val, length_val, Constant("1", ElementaryType("uint256")), BinaryType.ADDITION
1384    )
1385    ir_add_1.set_expression(ir.expression)
1386    ir_add_1.set_node(ir.node)
1387    ret.append(ir_add_1)
1388
1389    ir_assign_length = Assignment(length, new_length_val, ElementaryType("uint256"))
1390    ir_assign_length.set_expression(ir.expression)
1391    ir_assign_length.set_node(ir.node)
1392    ret.append(ir_assign_length)
1393
1394    return length_val
1397def convert_to_push_set_val(
1398    ir: HighLevelCall,
1399    node: "Node",
1400    length_val: TemporaryVariable,
1401    ret: List[
1402        Union[
1403            Length,
1404            Assignment,
1405            Binary,
1406        ]
1407    ],
1408) -> None:
1409    arr = ir.destination
1410
1411    new_type = ir.destination.type.type
1412
1413    element_to_add = ReferenceVariable(node)
1414    element_to_add.set_type(new_type)
1415    ir_assign_element_to_add = Index(element_to_add, arr, length_val)
1416    ir_assign_element_to_add.set_expression(ir.expression)
1417    ir_assign_element_to_add.set_node(ir.node)
1418    ret.append(ir_assign_element_to_add)
1419
1420    if len(ir.arguments) > 0:
1421        assign_value = ir.arguments[0]
1422        if isinstance(assign_value, list):
1423            assign_value = TemporaryVariable(node)
1424            assign_value.set_type(element_to_add.type)
1425            ir_assign_value = InitArray(ir.arguments[0], assign_value)
1426            ir_assign_value.set_expression(ir.expression)
1427            ir_assign_value.set_node(ir.node)
1428            ret.append(ir_assign_value)
1429
1430        ir_assign_value = Assignment(element_to_add, assign_value, assign_value.type)
1431        ir_assign_value.set_expression(ir.expression)
1432        ir_assign_value.set_node(ir.node)
1433        ret.append(ir_assign_value)
1434    else:
1435        new_element = ir.lvalue
1436        new_element.set_type(new_type)
1437        ir_assign_value = Assignment(new_element, element_to_add, new_type)
1438        ir_assign_value.set_expression(ir.expression)
1439        ir_assign_value.set_node(ir.node)
1440        ret.append(ir_assign_value)
1443def convert_to_push(
1444    ir: HighLevelCall, node: "Node"
1445) -> List[Union[Length, Assignment, Binary, Index, InitArray,]]:
1446    """
1447    Convert a call to a series of operations to push a new value onto the array
1448
1449    The function assume to receive a correct IR
1450    The checks must be done by the caller
1451
1452    May necessitate to create an intermediate operation (InitArray)
1453    Necessitate to return the length (see push documentation)
1454    As a result, the function return may return a list
1455    """
1456
1457    ret = []
1458
1459    length_val = convert_to_push_expand_arr(ir, node, ret)
1460    convert_to_push_set_val(ir, node, length_val, ret)
1461
1462    return ret

Convert a call to a series of operations to push a new value onto the array

The function assume to receive a correct IR The checks must be done by the caller

May necessitate to create an intermediate operation (InitArray) Necessitate to return the length (see push documentation) As a result, the function return may return a list

1465def convert_to_pop(ir: HighLevelCall, node: "Node") -> List[Operation]:
1466    """
1467    Convert pop operators
1468    Return a list of 6 operations
1469    """
1470
1471    ret: List[Operation] = []
1472
1473    arr = ir.destination
1474    length = ReferenceVariable(node)
1475    length.set_type(ElementaryType("uint256"))
1476
1477    ir_length = Length(arr, length)
1478    assert ir.expression
1479    ir_length.set_expression(ir.expression)
1480    ir_length.set_node(ir.node)
1481    length.points_to = arr
1482    ret.append(ir_length)
1483
1484    val = TemporaryVariable(node)
1485
1486    ir_sub_1 = Binary(val, length, Constant("1", ElementaryType("uint256")), BinaryType.SUBTRACTION)
1487    ir_sub_1.set_expression(ir.expression)
1488    ir_sub_1.set_node(ir.node)
1489    ret.append(ir_sub_1)
1490
1491    element_to_delete = ReferenceVariable(node)
1492    ir_assign_element_to_delete = Index(element_to_delete, arr, val)
1493    # TODO the following is equivalent to length.points_to = arr
1494    # Should it be removed?
1495    ir_length.lvalue.points_to = arr
1496    # Note bytes is an ElementaryType not ArrayType and bytes1 should be returned
1497    # since bytes is bytes1[] without padding between the elements
1498    # while in other cases such as uint256[] (ArrayType) we use ir.destination.type.type
1499    # in this way we will have the type always set to the corresponding ElementaryType
1500    element_to_delete.set_type(
1501        ElementaryType("bytes1")
1502        if isinstance(ir.destination.type, ElementaryType)
1503        else ir.destination.type.type
1504    )
1505    ir_assign_element_to_delete.set_expression(ir.expression)
1506    ir_assign_element_to_delete.set_node(ir.node)
1507    ret.append(ir_assign_element_to_delete)
1508
1509    ir_delete = Delete(element_to_delete, element_to_delete)
1510    ir_delete.set_expression(ir.expression)
1511    ir_delete.set_node(ir.node)
1512    ret.append(ir_delete)
1513
1514    length_to_assign = ReferenceVariable(node)
1515    length_to_assign.set_type(ElementaryType("uint256"))
1516    ir_length = Length(arr, length_to_assign)
1517    ir_length.set_expression(ir.expression)
1518    length_to_assign.points_to = arr
1519    ir_length.set_node(ir.node)
1520    ret.append(ir_length)
1521
1522    ir_assign_length = Assignment(length_to_assign, val, ElementaryType("uint256"))
1523    ir_assign_length.set_expression(ir.expression)
1524    ir_assign_length.set_node(ir.node)
1525    ret.append(ir_assign_length)
1526
1527    return ret

Convert pop operators Return a list of 6 operations

1530def look_for_library_or_top_level(
1531    ir: HighLevelCall,
1532    using_for,
1533    t: Union[
1534        UserDefinedType,
1535        ElementaryType,
1536        str,
1537        TypeAliasTopLevel,
1538    ],
1539) -> Optional[Union[LibraryCall, InternalCall,]]:
1540    for destination in using_for[t]:
1541        if isinstance(destination, FunctionTopLevel) and destination.name == ir.function_name:
1542            arguments = [ir.destination] + ir.arguments
1543            if (
1544                len(destination.parameters) == len(arguments)
1545                and _find_function_from_parameter(arguments, [destination], True) is not None
1546            ):
1547                internalcall = InternalCall(destination, ir.nbr_arguments, ir.lvalue, ir.type_call)
1548                internalcall.set_expression(ir.expression)
1549                internalcall.set_node(ir.node)
1550                internalcall.arguments = [ir.destination] + ir.arguments
1551                return_type = internalcall.function.return_type
1552                if return_type:
1553                    if len(return_type) == 1:
1554                        internalcall.lvalue.set_type(return_type[0])
1555                    elif len(return_type) > 1:
1556                        internalcall.lvalue.set_type(return_type)
1557                else:
1558                    internalcall.lvalue = None
1559                return internalcall
1560
1561        lib_contract = None
1562        if isinstance(destination, FunctionContract) and destination.contract.is_library:
1563            lib_contract = destination.contract
1564        elif isinstance(destination, UserDefinedType) and isinstance(destination.type, Contract):
1565            lib_contract = destination.type
1566
1567        if lib_contract:
1568            lib_call = LibraryCall(
1569                lib_contract,
1570                ir.function_name,
1571                ir.nbr_arguments,
1572                ir.lvalue,
1573                ir.type_call,
1574                names=ir.names,
1575            )
1576            lib_call.set_expression(ir.expression)
1577            lib_call.set_node(ir.node)
1578            lib_call.call_gas = ir.call_gas
1579            lib_call.arguments = [ir.destination] + ir.arguments
1580            new_ir = convert_type_library_call(lib_call, lib_contract)
1581            if new_ir:
1582                new_ir.set_node(ir.node)
1583                return new_ir
1584    return None
1587def convert_to_library_or_top_level(
1588    ir: HighLevelCall, node: "Node", using_for
1589) -> Optional[Union[LibraryCall, InternalCall,]]:
1590    t = ir.destination.type
1591    if t in using_for:
1592        new_ir = look_for_library_or_top_level(ir, using_for, t)
1593        if new_ir:
1594            return new_ir
1595
1596    if "*" in using_for:
1597        new_ir = look_for_library_or_top_level(ir, using_for, "*")
1598        if new_ir:
1599            return new_ir
1600
1601    if (
1602        isinstance(t, ElementaryType)
1603        and t.name == "address"
1604        and ir.destination.name == "this"
1605        and UserDefinedType(node.function.contract) in using_for
1606    ):
1607        new_ir = look_for_library_or_top_level(
1608            ir, using_for, UserDefinedType(node.function.contract)
1609        )
1610        if new_ir:
1611            return new_ir
1612
1613    return None
1616def get_type(
1617    t: Union[
1618        UserDefinedType,
1619        ElementaryType,
1620    ]
1621) -> str:
1622    """
1623    Convert a type to a str
1624    If the instance is a Contract, return 'address' instead
1625    """
1626    if isinstance(t, UserDefinedType):
1627        if isinstance(t.type, Contract):
1628            return "address"
1629    return str(t)

Convert a type to a str If the instance is a Contract, return 'address' instead

1640def convert_type_library_call(ir: HighLevelCall, lib_contract: Contract) -> Optional[LibraryCall]:
1641    func = None
1642    candidates = [
1643        f
1644        for f in lib_contract.functions
1645        if f.name == ir.function_name
1646        and not f.is_shadowed
1647        and len(f.parameters) == len(ir.arguments)
1648    ]
1649
1650    if len(candidates) == 1:
1651        func = candidates[0]
1652    # We can discard if there are arguments here because libraries only support constant variables
1653    # And constant variables cannot have non-value type
1654    # i.e. "uint[2] constant arr = [1,2];" is not possible in Solidity
1655    # If this were to change, the following condition might be broken
1656    if func is None and not ir.arguments:
1657        # TODO: handle collision with multiple state variables/functions
1658        func = lib_contract.get_state_variable_from_name(ir.function_name)
1659    if func is None and candidates:
1660        func = _find_function_from_parameter(ir.arguments, candidates, False)
1661
1662    # In case of multiple binding to the same type
1663    # TODO: this part might not be needed with _find_function_from_parameter
1664    if not func:
1665        # specific lookup when the compiler does implicit conversion
1666        # for example
1667        # myFunc(uint)
1668        # can be called with an uint8
1669        for function in lib_contract.functions:
1670            if function.name == ir.function_name and len(function.parameters) == len(ir.arguments):
1671                func = function
1672                break
1673    if not func:
1674        return None
1675    ir.function = func
1676    if isinstance(func, Function):
1677        t = func.return_type
1678        # if its not a tuple, return a singleton
1679        if t and len(t) == 1:
1680            t = t[0]
1681    else:
1682        # otherwise its a variable (getter)
1683        t = func.type
1684    if t:
1685        ir.lvalue.set_type(t)
1686    else:
1687        ir.lvalue = None
1688    return ir
def convert_type_of_high_and_internal_level_call( ir: slither.slithir.operations.operation.Operation, contract: Union[slither.core.declarations.contract.Contract, NoneType]) -> Union[slither.slithir.operations.operation.Operation, NoneType]:
1732def convert_type_of_high_and_internal_level_call(
1733    ir: Operation, contract: Optional[Contract]
1734) -> Optional[Operation]:
1735    """
1736    Convert the IR type based on heuristic
1737
1738    Args:
1739        ir: target
1740        contract: optional contract. This should be the target of the IR. It will be used to look up potential functions
1741
1742    Returns:
1743        Potential new IR
1744    """
1745
1746    func = None
1747    if isinstance(ir, InternalCall):
1748        candidates: List[Function]
1749        if ir.function_candidates:
1750            # This path is taken only for SolidityImportPlaceHolder
1751            # Here we have already done a filtering on the potential targets
1752            candidates = ir.function_candidates
1753        else:
1754            candidates = [
1755                f
1756                for f in contract.functions
1757                if f.name == ir.function_name
1758                and f.contract_declarer.name == ir.contract_name
1759                and len(f.parameters) == len(ir.arguments)
1760            ]
1761
1762            for import_statement in contract.file_scope.imports:
1763                if (
1764                    import_statement.alias is not None
1765                    and import_statement.alias == ir.contract_name
1766                ):
1767                    imported_scope = contract.compilation_unit.get_scope(import_statement.filename)
1768                    candidates += [
1769                        f
1770                        for f in list(imported_scope.functions)
1771                        if f.name == ir.function_name and len(f.parameters) == len(ir.arguments)
1772                    ]
1773
1774        func = _find_function_from_parameter(ir.arguments, candidates, False)
1775
1776        if not func:
1777            assert contract
1778            func = contract.get_state_variable_from_name(ir.function_name)
1779    else:
1780        assert isinstance(ir, HighLevelCall)
1781        assert contract
1782        candidates = [
1783            f
1784            for f in contract.functions
1785            if f.name == ir.function_name
1786            and not f.is_shadowed
1787            and len(f.parameters) == len(ir.arguments)
1788        ]
1789        if len(candidates) == 1:
1790            func = candidates[0]
1791        if func is None:
1792            # TODO: handle collision with multiple state variables/functions
1793            func = contract.get_state_variable_from_name(ir.function_name)
1794        if func is None and candidates:
1795            func = _find_function_from_parameter(ir.arguments, candidates, False)
1796
1797    # low level lookup needs to be done as last step
1798    if not func:
1799        if can_be_low_level(ir):
1800            return convert_to_low_level(ir)
1801        if can_be_solidity_func(ir):
1802            return convert_to_solidity_func(ir)
1803    if not func:
1804        to_log = f"Function not found {ir.function_name}"
1805        logger.error(to_log)
1806    ir.function = func
1807    if isinstance(func, Function):
1808        return_type = func.return_type
1809        # if its not a tuple; return a singleton
1810        if return_type and len(return_type) == 1:
1811            return_type = return_type[0]
1812    else:
1813        # otherwise its a variable (getter)
1814        # If its a mapping or a array
1815        # we iterate until we find the final type
1816        # mapping and array can be mixed together
1817        # ex:
1818        #    mapping ( uint => mapping ( uint => uint)) my_var
1819        #    mapping(uint => uint)[] test;p
1820        if isinstance(func.type, (MappingType, ArrayType)):
1821            tmp = func.type
1822            while isinstance(tmp, (MappingType, ArrayType)):
1823                if isinstance(tmp, MappingType):
1824                    tmp = tmp.type_to
1825                else:
1826                    tmp = tmp.type
1827            return_type = tmp
1828        else:
1829            return_type = func.type
1830    if return_type:
1831
1832        # If the return type is a structure, but the lvalue is a tuple
1833        # We convert the type of the structure to a list of element
1834        # TODO: explore to replace all tuple variables by structures
1835        if (
1836            isinstance(ir.lvalue, TupleVariable)
1837            and isinstance(return_type, UserDefinedType)
1838            and isinstance(return_type.type, Structure)
1839        ):
1840            return_type = _convert_to_structure_to_list(return_type)
1841
1842        ir.lvalue.set_type(return_type)
1843    else:
1844        ir.lvalue = None
1845
1846    return None

Convert the IR type based on heuristic

Args: ir: target contract: optional contract. This should be the target of the IR. It will be used to look up potential functions

Returns: Potential new IR

def find_references_origin(irs: List[slither.slithir.operations.operation.Operation]) -> None:
1857def find_references_origin(irs: List[Operation]) -> None:
1858    """
1859    Make lvalue of each Index, Member operation
1860    points to the left variable
1861    """
1862    for ir in irs:
1863        if isinstance(ir, (Index, Member)):
1864            ir.lvalue.points_to = ir.variable_left

Make lvalue of each Index, Member operation points to the left variable

def remove_temporary(result):
1875def remove_temporary(result):
1876    result = [
1877        ins
1878        for ins in result
1879        if not isinstance(
1880            ins,
1881            (
1882                Argument,
1883                TmpNewElementaryType,
1884                TmpNewContract,
1885                TmpNewArray,
1886                TmpNewStructure,
1887            ),
1888        )
1889    ]
1890
1891    return result
1894def remove_unused(result: List[Operation]) -> List[Operation]:
1895    removed = True
1896
1897    if not result:
1898        return result
1899
1900    # dont remove the last elem, as it may be used by RETURN
1901    last_elem = result[-1]
1902
1903    while removed:
1904        removed = False
1905
1906        to_keep = []
1907        to_remove = []
1908
1909        # keep variables that are read
1910        # and reference that are written
1911        for ins in result:
1912            to_keep += [str(x) for x in ins.read]
1913            if isinstance(ins, OperationWithLValue) and not isinstance(ins, (Index, Member)):
1914                if isinstance(ins.lvalue, ReferenceVariable):
1915                    to_keep += [str(ins.lvalue)]
1916
1917        for ins in result:
1918            if isinstance(ins, Member):
1919                if not ins.lvalue.name in to_keep and ins != last_elem:
1920                    to_remove.append(ins)
1921                    removed = True
1922            # Remove type(X) if X is an elementary type
1923            # This assume that type(X) is only used with min/max
1924            # If Solidity introduces other operation, we might remove this removal
1925            if isinstance(ins, SolidityCall) and ins.function == SolidityFunction("type()"):
1926                if isinstance(ins.arguments[0], ElementaryType):
1927                    to_remove.append(ins)
1928
1929        result = [i for i in result if not i in to_remove]
1930    return result
def convert_constant_types(irs: List[slither.slithir.operations.operation.Operation]) -> None:
1941def convert_constant_types(irs: List[Operation]) -> None:
1942    """
1943    late conversion of uint -> type for constant (Literal)
1944    :param irs:
1945    :return:
1946    """
1947    # TODO: implement instances lookup for events, NewContract
1948    was_changed = True
1949    while was_changed:
1950        was_changed = False
1951        for ir in irs:
1952            if isinstance(ir, (Assignment, Binary)):
1953                if (
1954                    isinstance(ir.lvalue.type, ElementaryType)
1955                    and ir.lvalue.type.type in ElementaryTypeInt
1956                ):
1957                    for r in ir.read:
1958                        if isinstance(r, Constant) and r.type.type not in ElementaryTypeInt:
1959                            r.set_type(ElementaryType(ir.lvalue.type.type))
1960                            was_changed = True
1961
1962            if isinstance(ir, (HighLevelCall, InternalCall)):
1963                func = ir.function
1964                if isinstance(func, StateVariable):
1965                    types = export_nested_types_from_variable(func)
1966                else:
1967                    types = [p.type for p in func.parameters]
1968                assert len(types) == len(ir.arguments)
1969                for idx, arg in enumerate(ir.arguments):
1970                    t = types[idx]
1971                    if isinstance(t, ElementaryType):
1972                        if t.type in ElementaryTypeInt:
1973                            if arg.type.type not in ElementaryTypeInt:
1974                                arg.set_type(ElementaryType(t.type))
1975                                was_changed = True
1976
1977            if isinstance(ir, NewStructure):
1978                st = ir.structure
1979                for idx, arg in enumerate(ir.arguments):
1980                    e = st.elems_ordered[idx]
1981                    if isinstance(e.type, ElementaryType):
1982                        if e.type.type in ElementaryTypeInt:
1983                            if arg.type.type not in ElementaryTypeInt:
1984                                arg.set_type(ElementaryType(e.type.type))
1985                                was_changed = True
1986
1987            def is_elementary_array(t):
1988                return isinstance(t, ArrayType) and isinstance(t.type, ElementaryType)
1989
1990            if isinstance(ir, InitArray):
1991                if is_elementary_array(ir.lvalue.type):
1992                    if ir.lvalue.type.type.type in ElementaryTypeInt:
1993                        for r in ir.read:
1994                            if isinstance(r, Constant) and is_elementary_array(r.type):
1995                                if r.type.type.type not in ElementaryTypeInt:
1996                                    r.set_type(ElementaryType(ir.lvalue.type.type.type))
1997                                    was_changed = True

late conversion of uint -> type for constant (Literal)

Parameters
  • irs:
Returns
def convert_delete(irs: List[slither.slithir.operations.operation.Operation]) -> None:
2008def convert_delete(irs: List[Operation]) -> None:
2009    """
2010    Convert the lvalue of the Delete to point to the variable removed
2011    This can only be done after find_references_origin is called
2012    :param irs:
2013    :return:
2014    """
2015    for ir in irs:
2016        if isinstance(ir, Delete):
2017            if isinstance(ir.lvalue, ReferenceVariable):
2018                ir.lvalue = ir.lvalue.points_to

Convert the lvalue of the Delete to point to the variable removed This can only be done after find_references_origin is called

Parameters
  • irs:
Returns
def apply_ir_heuristics( irs: List[slither.slithir.operations.operation.Operation], node: slither.core.cfg.node.Node, is_solidity: bool) -> List[slither.slithir.operations.operation.Operation]:
2047def apply_ir_heuristics(irs: List[Operation], node: "Node", is_solidity: bool) -> List[Operation]:
2048    """
2049    Apply a set of heuristic to improve slithIR
2050    """
2051
2052    irs = integrate_value_gas(irs)
2053
2054    irs = propagate_type_and_convert_call(irs, node)
2055    irs = remove_unused(irs)
2056    find_references_origin(irs)
2057
2058    # These are heuristics that are only applied to Solidity
2059    if is_solidity:
2060        convert_constant_types(irs)
2061        convert_delete(irs)
2062
2063    _find_source_mapping_references(irs)
2064
2065    return irs

Apply a set of heuristic to improve slithIR