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

Integrate value and gas temporary arguments to call instruction

389def get_declared_param_names(
390    ins: Union[
391        NewStructure,
392        NewContract,
393        InternalCall,
394        LibraryCall,
395        HighLevelCall,
396        InternalDynamicCall,
397        EventCall,
398    ]
399) -> Optional[List[str]]:
400    """
401    Given a call operation, return the list of parameter names, in the order
402    listed in the function declaration.
403    #### Parameters
404    ins -
405        The call instruction
406    #### Possible Returns
407    List[str] -
408        A list of the parameters in declaration order
409    None -
410        Workaround: Unable to obtain list of parameters in declaration order
411    """
412    if isinstance(ins, NewStructure):
413        return [x.name for x in ins.structure.elems_ordered if not isinstance(x.type, MappingType)]
414    if isinstance(ins, (InternalCall, LibraryCall, HighLevelCall)):
415        if isinstance(ins.function, Function):
416            return [p.name for p in ins.function.parameters]
417        return None
418    if isinstance(ins, InternalDynamicCall):
419        return [p.name for p in ins.function_type.params]
420
421    assert isinstance(ins, (EventCall, NewContract))
422    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[str] - A list of the parameters in declaration order 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[str]) -> List[slither.core.variables.variable.Variable]:
425def reorder_arguments(
426    args: List[Variable], call_names: List[str], decl_names: List[str]
427) -> List[Variable]:
428    """
429    Reorder named struct constructor arguments so that they match struct declaration ordering rather
430    than call ordering
431    E.g. for `struct S { int x; int y; }` we reorder `S({y : 2, x : 3})` to `S(3, 2)`
432    #### Parameters
433    args -
434        Arguments to constructor call, in call order
435    names -
436        Parameter names in call order
437    decl_names -
438        Parameter names in declaration order
439    #### Returns
440    Reordered arguments to constructor call, now in declaration order
441    """
442    assert len(args) == len(call_names)
443    assert len(call_names) == len(decl_names)
444
445    args_ret = []
446    for n in decl_names:
447        ind = call_names.index(n)
448        args_ret.append(args[ind])
449
450    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 - 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]:
453def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> List[Operation]:
454    """
455    Propagate the types variables and convert tmp call to real call operation
456    """
457    calls_value = {}
458    calls_gas = {}
459
460    call_data = []
461
462    idx = 0
463    # use of while len() as result can be modified during the iteration
464    while idx < len(result):
465        ins = result[idx]
466
467        if isinstance(ins, TmpCall):
468            # If the node.function is a FunctionTopLevel, then it does not have a contract
469            contract = (
470                node.function.contract if isinstance(node.function, FunctionContract) else None
471            )
472            new_ins = extract_tmp_call(ins, contract)
473            if new_ins:
474                new_ins.set_node(ins.node)
475                ins = new_ins
476                result[idx] = ins
477
478        # If there's two consecutive type conversions, keep only the last
479        # ARG_CALL x                             call_data = [x]
480        # TMP_4 = CONVERT x to Fix               call_data = []
481        # TMP_5 = CONVERT TMP_4 to uint192       call_data = [TMP_5]
482        if isinstance(ins, TypeConversion) and ins.variable in call_data:
483            call_data.remove(ins.variable)
484
485        if isinstance(ins, Argument):
486            # In case of dupplicate arguments we overwrite the value
487            # This can happen because of addr.call.value(1).value(2)
488            if ins.get_type() in [ArgumentType.GAS]:
489                calls_gas[ins.call_id] = ins.argument
490            elif ins.get_type() in [ArgumentType.VALUE]:
491                calls_value[ins.call_id] = ins.argument
492            else:
493                assert ins.get_type() == ArgumentType.CALL
494                call_data.append(ins.argument)
495
496        if isinstance(ins, (HighLevelCall, NewContract, InternalDynamicCall)):
497            if ins.call_id in calls_value:
498                ins.call_value = calls_value[ins.call_id]
499            if ins.call_id in calls_gas and isinstance(ins, (HighLevelCall, InternalDynamicCall)):
500                ins.call_gas = calls_gas[ins.call_id]
501
502        if isinstance(ins, Call) and (ins.names is not None):
503            assert isinstance(
504                ins,
505                (
506                    NewStructure,
507                    NewContract,
508                    InternalCall,
509                    LibraryCall,
510                    HighLevelCall,
511                    InternalDynamicCall,
512                    EventCall,
513                ),
514            )
515            decl_param_names = get_declared_param_names(ins)
516            if decl_param_names is not None:
517                call_data = reorder_arguments(call_data, ins.names, decl_param_names)
518
519        if isinstance(ins, (Call, NewContract, NewStructure)):
520            # We might have stored some arguments for libraries
521            if ins.arguments:
522                call_data = ins.arguments + call_data
523            ins.arguments = call_data
524            call_data = []
525
526        if is_temporary(ins):
527            del result[idx]
528            continue
529
530        new_ins = propagate_types(ins, node)
531        if new_ins:
532            if isinstance(new_ins, (list,)):
533                for new_ins_ in new_ins:
534                    new_ins_.set_node(ins.node)
535                del result[idx]
536                for i, ins in enumerate(new_ins):
537                    result.insert(idx + i, ins)
538                idx = idx + len(new_ins) - 1
539            else:
540                new_ins.set_node(ins.node)
541                result[idx] = new_ins
542        idx = idx + 1
543    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):
595def propagate_types(ir: Operation, node: "Node"):  # pylint: disable=too-many-locals
596    # propagate the type
597    node_function = node.function
598
599    using_for: USING_FOR = {}
600    if isinstance(node_function, FunctionContract):
601        using_for = node_function.contract.using_for_complete
602    elif isinstance(node_function, FunctionTopLevel):
603        using_for = node_function.using_for_complete
604
605    if isinstance(ir, OperationWithLValue) and ir.lvalue:
606        # Force assignment in case of missing previous correct type
607        if not ir.lvalue.type:
608            if isinstance(ir, Assignment):
609                ir.lvalue.set_type(ir.rvalue.type)
610            elif isinstance(ir, Binary):
611                if BinaryType.return_bool(ir.type):
612                    ir.lvalue.set_type(ElementaryType("bool"))
613                else:
614                    ir.lvalue.set_type(ir.variable_left.type)
615            elif isinstance(ir, Delete):
616                # nothing to propagate
617                pass
618            elif isinstance(ir, LibraryCall):
619                return convert_type_library_call(ir, ir.destination)
620            elif isinstance(ir, HighLevelCall):
621                t = ir.destination.type
622                # Temporary operation (they are removed later)
623                if t is None:
624                    return None
625
626                if isinstance(t, ElementaryType) and t.name == "address":
627                    if can_be_solidity_func(ir):
628                        return convert_to_solidity_func(ir)
629
630                # convert library or top level function
631                if t in using_for or "*" in using_for:
632                    new_ir = convert_to_library_or_top_level(ir, node, using_for)
633                    if new_ir:
634                        return new_ir
635
636                # convert library function when used with "this"
637                if (
638                    isinstance(t, ElementaryType)
639                    and t.name == "address"
640                    and ir.destination.name == "this"
641                    and UserDefinedType(node_function.contract) in using_for
642                ):
643                    new_ir = convert_to_library_or_top_level(ir, node, using_for)
644                    if new_ir:
645                        return new_ir
646
647                if isinstance(t, UserDefinedType):
648                    # UserdefinedType
649                    t_type = t.type
650                    if isinstance(t_type, Contract):
651                        # the target contract of the IR is the t_type (the destination of the call)
652                        return convert_type_of_high_and_internal_level_call(ir, t_type)
653
654                # Convert HighLevelCall to LowLevelCall
655                if (isinstance(t, ElementaryType) and t.name == "address") or (
656                    isinstance(t, TypeAlias) and t.underlying_type.name == "address"
657                ):
658                    if ir.destination.name == "this":
659                        # Cannot be a top level function with this.
660                        assert isinstance(node_function, FunctionContract)
661                        # the target contract is the contract itself
662                        return convert_type_of_high_and_internal_level_call(
663                            ir, node_function.contract
664                        )
665                    if can_be_low_level(ir):
666                        return convert_to_low_level(ir)
667
668                # Convert push operations
669                # May need to insert a new operation
670                # Which leads to return a list of operation
671                if isinstance(t, ArrayType) or (
672                    isinstance(t, ElementaryType) and t.type == "bytes"
673                ):
674                    # Solidity uses push
675                    # Vyper uses append
676                    if ir.function_name in ["push", "append"] and len(ir.arguments) <= 1:
677                        return convert_to_push(ir, node)
678                    if ir.function_name == "pop" and len(ir.arguments) == 0:
679                        return convert_to_pop(ir, node)
680
681            elif isinstance(ir, Index):
682                if isinstance(ir.variable_left.type, MappingType):
683                    ir.lvalue.set_type(ir.variable_left.type.type_to)
684                elif isinstance(ir.variable_left.type, ArrayType):
685                    ir.lvalue.set_type(ir.variable_left.type.type)
686
687            elif isinstance(ir, InitArray):
688                length = len(ir.init_values)
689                t = ir.init_values[0].type
690                ir.lvalue.set_type(ArrayType(t, length))
691            elif isinstance(ir, InternalCall):
692                # if its not a tuple, return a singleton
693                if ir.function is None:
694                    func = node.function
695                    function_contract = (
696                        func.contract if isinstance(func, FunctionContract) else None
697                    )
698                    # the target contract might be None if its a top level function
699                    convert_type_of_high_and_internal_level_call(ir, function_contract)
700                return_type = ir.function.return_type
701                if return_type:
702                    if len(return_type) == 1:
703                        ir.lvalue.set_type(return_type[0])
704                    elif len(return_type) > 1:
705                        ir.lvalue.set_type(return_type)
706                else:
707                    ir.lvalue = None
708            elif isinstance(ir, InternalDynamicCall):
709                # if its not a tuple, return a singleton
710                return_type = ir.function_type.return_type
711                if return_type:
712                    if len(return_type) == 1:
713                        ir.lvalue.set_type(return_type[0])
714                    else:
715                        ir.lvalue.set_type(return_type)
716                else:
717                    ir.lvalue = None
718            elif isinstance(ir, LowLevelCall):
719                # Call are not yet converted
720                # This should not happen
721                assert False
722            elif isinstance(ir, Member):
723                # TODO we should convert the reference to a temporary if the member is a length or a balance
724                if (
725                    ir.variable_right == "length"
726                    and not isinstance(ir.variable_left, Contract)
727                    and isinstance(ir.variable_left.type, (ElementaryType, ArrayType))
728                ):
729                    new_length = Length(ir.variable_left, ir.lvalue)
730                    assert ir.expression
731                    new_length.set_expression(ir.expression)
732                    new_length.lvalue.points_to = ir.variable_left
733                    new_length.set_node(ir.node)
734                    return new_length
735                # This only happen for .balance/code/codehash access on a variable for which we dont know at
736                # early parsing time the type
737                # Like
738                # function return_addr() internal returns(addresss)
739                #
740                # return_addr().balance
741                # Here slithIR will incorrectly create a REF variable instead of a Temp variable
742                # However this pattern does not appear so often
743                if (
744                    ir.variable_right.name in ["balance", "code", "codehash"]
745                    and not isinstance(ir.variable_left, Contract)
746                    and isinstance(ir.variable_left.type, ElementaryType)
747                ):
748                    name = ir.variable_right.name + "(address)"
749                    sol_func = SolidityFunction(name)
750                    s = SolidityCall(
751                        sol_func,
752                        1,
753                        ir.lvalue,
754                        sol_func.return_type,
755                    )
756                    s.arguments.append(ir.variable_left)
757                    s.set_expression(ir.expression)
758                    s.lvalue.set_type(sol_func.return_type)
759                    s.set_node(ir.node)
760                    return s
761                if (
762                    ir.variable_right == "codesize"
763                    and not isinstance(ir.variable_left, Contract)
764                    and isinstance(ir.variable_left.type, ElementaryType)
765                ):
766                    b = CodeSize(ir.variable_left, ir.lvalue)
767                    b.set_expression(ir.expression)
768                    b.set_node(ir.node)
769                    return b
770                if ir.variable_right == "selector" and isinstance(ir.variable_left, (CustomError)):
771                    assignment = Assignment(
772                        ir.lvalue,
773                        Constant(
774                            str(get_function_id(ir.variable_left.solidity_signature)),
775                            ElementaryType("bytes4"),
776                        ),
777                        ElementaryType("bytes4"),
778                    )
779                    assignment.set_expression(ir.expression)
780                    assignment.set_node(ir.node)
781                    assignment.lvalue.set_type(ElementaryType("bytes4"))
782                    return assignment
783
784                if isinstance(ir.variable_right, (CustomError)):
785                    assignment = Assignment(
786                        ir.lvalue,
787                        Constant(
788                            str(get_function_id(ir.variable_left.solidity_signature)),
789                            ElementaryType("bytes4"),
790                        ),
791                        ElementaryType("bytes4"),
792                    )
793                    assignment.set_expression(ir.expression)
794                    assignment.set_node(ir.node)
795                    assignment.lvalue.set_type(ElementaryType("bytes4"))
796                    return assignment
797                if ir.variable_right == "selector" and isinstance(
798                    ir.variable_left.type, (Function)
799                ):
800                    assignment = Assignment(
801                        ir.lvalue,
802                        Constant(str(get_function_id(ir.variable_left.type.full_name))),
803                        ElementaryType("bytes4"),
804                    )
805                    assignment.set_expression(ir.expression)
806                    assignment.set_node(ir.node)
807                    assignment.lvalue.set_type(ElementaryType("bytes4"))
808                    return assignment
809                if isinstance(ir.variable_left, TemporaryVariable) and isinstance(
810                    ir.variable_left.type, TypeInformation
811                ):
812                    return _convert_type_contract(ir)
813                left = ir.variable_left
814                t = None
815                ir_func = ir.node.function
816                # Handling of this.function_name usage
817                if (
818                    left == SolidityVariable("this")
819                    and isinstance(ir.variable_right, Constant)
820                    and isinstance(ir_func, FunctionContract)
821                    and str(ir.variable_right) in [x.name for x in ir_func.contract.functions]
822                ):
823                    # Assumption that this.function_name can only compile if
824                    # And the contract does not have two functions starting with function_name
825                    # Otherwise solc raises:
826                    # Error: Member "f" not unique after argument-dependent lookup in contract
827                    targeted_function = next(
828                        (x for x in ir_func.contract.functions if x.name == str(ir.variable_right))
829                    )
830                    t = _make_function_type(targeted_function)
831                    ir.lvalue.set_type(t)
832                elif isinstance(left, (Variable, SolidityVariable)):
833                    t = ir.variable_left.type
834                elif isinstance(left, (Contract, Enum, Structure)):
835                    t = UserDefinedType(left)
836                # can be None due to temporary operation
837                if t:
838                    if isinstance(t, UserDefinedType):
839                        # UserdefinedType
840                        type_t = t.type
841                        if isinstance(type_t, Enum):
842                            ir.lvalue.set_type(t)
843                        elif isinstance(type_t, Structure):
844                            elems = type_t.elems
845                            for elem in elems:
846                                if elem == ir.variable_right:
847                                    ir.lvalue.set_type(elems[elem].type)
848                        else:
849                            assert isinstance(type_t, Contract)
850                            # Allow type propagtion as a Function
851                            # Only for reference variables
852                            # This allows to track the selector keyword
853                            # We dont need to check for function collision, as solc prevents the use of selector
854                            # if there are multiple functions with the same name
855                            f = next(
856                                (f for f in type_t.functions if f.name == ir.variable_right),
857                                None,
858                            )
859                            if f:
860                                ir.lvalue.set_type(f)
861                            else:
862                                # Allow propgation for variable access through contract's name
863                                # like Base_contract.my_variable
864                                v = next(
865                                    (
866                                        v
867                                        for v in type_t.state_variables
868                                        if v.name == ir.variable_right
869                                    ),
870                                    None,
871                                )
872                                if v:
873                                    ir.lvalue.set_type(v.type)
874            elif isinstance(ir, NewArray):
875                ir.lvalue.set_type(ir.array_type)
876            elif isinstance(ir, NewContract):
877                ir.lvalue.set_type(ir.contract_name)
878            elif isinstance(ir, NewElementaryType):
879                ir.lvalue.set_type(ir.type)
880            elif isinstance(ir, NewStructure):
881                ir.lvalue.set_type(UserDefinedType(ir.structure))
882            elif isinstance(ir, Send):
883                ir.lvalue.set_type(ElementaryType("bool"))
884            elif isinstance(ir, SolidityCall):
885                if ir.function.name in ["type(address)", "type()"]:
886                    ir.function.return_type = [TypeInformation(ir.arguments[0])]
887                return_type = ir.function.return_type
888                if len(return_type) == 1:
889                    ir.lvalue.set_type(return_type[0])
890                elif len(return_type) > 1:
891                    ir.lvalue.set_type(return_type)
892            elif isinstance(ir, TypeConversion):
893                ir.lvalue.set_type(ir.type)
894            elif isinstance(ir, Unary):
895                ir.lvalue.set_type(ir.rvalue.type)
896            elif isinstance(ir, Unpack):
897                types = ir.tuple.type.type
898                idx = ir.index
899                t = types[idx]
900                ir.lvalue.set_type(t)
901            elif isinstance(
902                ir,
903                (
904                    Argument,
905                    TmpCall,
906                    TmpNewArray,
907                    TmpNewContract,
908                    TmpNewStructure,
909                    TmpNewElementaryType,
910                ),
911            ):
912                # temporary operation; they will be removed
913                pass
914            else:
915                raise SlithIRError(f"Not handling {type(ir)} during type propagation")
916    return None
 920def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, Nop]:
 921    assert isinstance(ins, TmpCall)
 922    if isinstance(ins.called, Variable) and isinstance(ins.called.type, FunctionType):
 923        # If the call is made to a variable member, where the member is this
 924        # We need to convert it to a HighLelelCall and not an internal dynamic call
 925        if isinstance(ins.ori, Member) and ins.ori.variable_left == SolidityVariable("this"):
 926            pass
 927        else:
 928            call = InternalDynamicCall(ins.lvalue, ins.called, ins.called.type)
 929            call.set_expression(ins.expression)
 930            call.call_id = ins.call_id
 931            return call
 932    if isinstance(ins.ori, Member):
 933        # If there is a call on an inherited contract, it is an internal call or an event
 934        if contract and ins.ori.variable_left in contract.inheritance + [contract]:
 935            if str(ins.ori.variable_right) in [f.name for f in contract.functions]:
 936                internalcall = InternalCall(
 937                    (ins.ori.variable_right, ins.ori.variable_left.name),
 938                    ins.nbr_arguments,
 939                    ins.lvalue,
 940                    ins.type_call,
 941                )
 942                internalcall.set_expression(ins.expression)
 943                internalcall.call_id = ins.call_id
 944                return internalcall
 945            if str(ins.ori.variable_right) in [f.name for f in contract.events]:
 946                eventcall = EventCall(ins.ori.variable_right)
 947                eventcall.set_expression(ins.expression)
 948                eventcall.call_id = ins.call_id
 949                return eventcall
 950        if isinstance(ins.ori.variable_left, Contract):
 951            st = ins.ori.variable_left.get_structure_from_name(ins.ori.variable_right)
 952            if st:
 953                op = NewStructure(st, ins.lvalue, names=ins.names)
 954                op.set_expression(ins.expression)
 955                op.call_id = ins.call_id
 956                return op
 957
 958            # For event emit through library
 959            # lib L { event E()}
 960            # ...
 961            # emit L.E();
 962            if str(ins.ori.variable_right) in [f.name for f in ins.ori.variable_left.events]:
 963                eventcall = EventCall(ins.ori.variable_right)
 964                eventcall.set_expression(ins.expression)
 965                eventcall.call_id = ins.call_id
 966                return eventcall
 967
 968            # lib Lib { error Error()} ... revert Lib.Error()
 969            if str(ins.ori.variable_right) in ins.ori.variable_left.custom_errors_as_dict:
 970                custom_error = ins.ori.variable_left.custom_errors_as_dict[
 971                    str(ins.ori.variable_right)
 972                ]
 973                assert isinstance(
 974                    custom_error,
 975                    CustomError,
 976                )
 977                sol_function = SolidityCustomRevert(custom_error)
 978                solidity_call = SolidityCall(
 979                    sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call
 980                )
 981                solidity_call.set_expression(ins.expression)
 982                return solidity_call
 983
 984            libcall = LibraryCall(
 985                ins.ori.variable_left,
 986                ins.ori.variable_right,
 987                ins.nbr_arguments,
 988                ins.lvalue,
 989                ins.type_call,
 990                names=ins.names,
 991            )
 992            libcall.set_expression(ins.expression)
 993            libcall.call_id = ins.call_id
 994            return libcall
 995        if isinstance(ins.ori.variable_left, Function):
 996            # Support for library call where the parameter is a function
 997            # We could merge this with the standard library handling
 998            # Except that we will have some troubles with using_for
 999            # As the type of the funciton will not match function()
1000            # Additionally we do not have a correct view on the parameters of the tmpcall
1001            # At this level
1002            #
1003            # library FunctionExtensions {
1004            #     function h(function() internal _t, uint8) internal {  }
1005            # }
1006            # contract FunctionMembers {
1007            #     using FunctionExtensions for function();
1008            #
1009            #     function f() public {
1010            #         f.h(1);
1011            #     }
1012            # }
1013            node_func = ins.node.function
1014            using_for = (
1015                node_func.contract.using_for_complete
1016                if isinstance(node_func, FunctionContract)
1017                else {}
1018            )
1019
1020            targeted_libraries = (
1021                [] + using_for.get("*", []) + using_for.get(FunctionType([], []), [])
1022            )
1023            lib_contract: Contract
1024            candidates = []
1025            for lib_contract_type in targeted_libraries:
1026                if not isinstance(lib_contract_type, UserDefinedType) and isinstance(
1027                    lib_contract_type.type, Contract
1028                ):
1029                    continue
1030                if isinstance(lib_contract_type, FunctionContract):
1031                    # Using for with list of functions, this is the function called
1032                    candidates.append(lib_contract_type)
1033                else:
1034                    lib_contract = lib_contract_type.type
1035                    for lib_func in lib_contract.functions:
1036                        if lib_func.name == ins.ori.variable_right:
1037                            candidates.append(lib_func)
1038
1039            if len(candidates) == 1:
1040                lib_func = candidates[0]
1041                # Library must be from a contract
1042                assert isinstance(lib_func, FunctionContract)
1043                lib_call = LibraryCall(
1044                    lib_func.contract,
1045                    Constant(lib_func.name),
1046                    len(lib_func.parameters),
1047                    ins.lvalue,
1048                    "d",
1049                    names=ins.names,
1050                )
1051                lib_call.set_expression(ins.expression)
1052                lib_call.set_node(ins.node)
1053                lib_call.call_gas = ins.call_gas
1054                lib_call.call_id = ins.call_id
1055                lib_call.set_node(ins.node)
1056                lib_call.function = lib_func
1057                lib_call.arguments.append(ins.ori.variable_left)
1058                return lib_call
1059            # We do not support something lik
1060            # library FunctionExtensions {
1061            #     function h(function() internal _t, uint8) internal {  }
1062            #     function h(function() internal _t, bool) internal {  }
1063            # }
1064            # contract FunctionMembers {
1065            #     using FunctionExtensions for function();
1066            #
1067            #     function f() public {
1068            #         f.h(1);
1069            #     }
1070            # }
1071            to_log = "Slither does not support dynamic functions to libraries if functions have the same name"
1072            to_log += f"{[candidate.full_name for candidate in candidates]}"
1073            raise SlithIRError(to_log)
1074        if isinstance(ins.ori.variable_left, SolidityImportPlaceHolder):
1075            # For top level import, where the import statement renames the filename
1076            # See https://blog.soliditylang.org/2020/09/02/solidity-0.7.1-release-announcement/
1077
1078            current_path = Path(ins.ori.variable_left.source_mapping.filename.absolute).parent
1079            target = str(
1080                Path(current_path, ins.ori.variable_left.import_directive.filename).absolute()
1081            )
1082            top_level_function_targets = [
1083                f
1084                for f in ins.compilation_unit.functions_top_level
1085                if f.source_mapping.filename.absolute == target
1086                and f.name == ins.ori.variable_right
1087                and len(f.parameters) == ins.nbr_arguments
1088            ]
1089
1090            internalcall = InternalCall(
1091                (ins.ori.variable_right, ins.ori.variable_left.name),
1092                ins.nbr_arguments,
1093                ins.lvalue,
1094                ins.type_call,
1095            )
1096            internalcall.set_expression(ins.expression)
1097            internalcall.call_id = ins.call_id
1098            internalcall.function_candidates = top_level_function_targets
1099            return internalcall
1100
1101        if ins.ori.variable_left == ElementaryType("bytes") and ins.ori.variable_right == Constant(
1102            "concat"
1103        ):
1104            s = SolidityCall(
1105                SolidityFunction("bytes.concat()"),
1106                ins.nbr_arguments,
1107                ins.lvalue,
1108                ins.type_call,
1109            )
1110            s.set_expression(ins.expression)
1111            return s
1112
1113        if ins.ori.variable_left == ElementaryType("string") and ins.ori.variable_right == Constant(
1114            "concat"
1115        ):
1116            s = SolidityCall(
1117                SolidityFunction("string.concat()"),
1118                ins.nbr_arguments,
1119                ins.lvalue,
1120                ins.type_call,
1121            )
1122            s.set_expression(ins.expression)
1123            return s
1124
1125        msgcall = HighLevelCall(
1126            ins.ori.variable_left,
1127            ins.ori.variable_right,
1128            ins.nbr_arguments,
1129            ins.lvalue,
1130            ins.type_call,
1131            names=ins.names,
1132        )
1133        msgcall.call_id = ins.call_id
1134
1135        if ins.call_gas:
1136            msgcall.call_gas = ins.call_gas
1137        if ins.call_value:
1138            msgcall.call_value = ins.call_value
1139        msgcall.set_expression(ins.expression)
1140
1141        return msgcall
1142
1143    if isinstance(ins.ori, TmpCall):
1144        r = extract_tmp_call(ins.ori, contract)
1145        r.set_node(ins.node)
1146        return r
1147    if isinstance(ins.called, SolidityVariableComposed):
1148        if str(ins.called) == "block.blockhash":
1149            ins.called = SolidityFunction("blockhash(uint256)")
1150
1151    if isinstance(ins.called, SolidityFunction):
1152        s = SolidityCall(ins.called, ins.nbr_arguments, ins.lvalue, ins.type_call)
1153        s.set_expression(ins.expression)
1154        return s
1155
1156    if isinstance(ins.called, CustomError):
1157        sol_function = SolidityCustomRevert(ins.called)
1158        s = SolidityCall(sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call)
1159        s.set_expression(ins.expression)
1160        return s
1161
1162    if isinstance(ins.ori, TmpNewElementaryType):
1163        n = NewElementaryType(ins.ori.type, ins.lvalue)
1164        n.set_expression(ins.expression)
1165        return n
1166
1167    if isinstance(ins.ori, TmpNewContract):
1168        op = NewContract(ins.ori.contract_name, ins.lvalue)
1169        op.set_expression(ins.expression)
1170        op.call_id = ins.call_id
1171        if ins.call_value:
1172            op.call_value = ins.call_value
1173        if ins.call_salt:
1174            op.call_salt = ins.call_salt
1175        return op
1176
1177    if isinstance(ins.ori, TmpNewArray):
1178        n = NewArray(ins.ori.array_type, ins.lvalue)
1179        n.set_expression(ins.expression)
1180        return n
1181
1182    if isinstance(ins.called, Structure):
1183        op = NewStructure(ins.called, ins.lvalue, names=ins.names)
1184        op.set_expression(ins.expression)
1185        op.call_id = ins.call_id
1186        op.set_expression(ins.expression)
1187        return op
1188
1189    if isinstance(ins.called, Event):
1190        e = EventCall(ins.called.name)
1191        e.set_expression(ins.expression)
1192        return e
1193
1194    if isinstance(ins.called, Contract):
1195        # Called a base constructor, where there is no constructor
1196        if ins.called.constructor is None:
1197            return Nop()
1198        # Case where:
1199        # contract A{ constructor(uint) }
1200        # contract B is A {}
1201        # contract C is B{ constructor() A(10) B() {}
1202        # C calls B(), which does not exist
1203        # Ideally we should compare here for the parameters types too
1204        if len(ins.called.constructor.parameters) != ins.nbr_arguments:
1205            return Nop()
1206        internalcall = InternalCall(
1207            ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call, ins.names
1208        )
1209        internalcall.call_id = ins.call_id
1210        internalcall.set_expression(ins.expression)
1211        return internalcall
1212
1213    raise SlithIRError(f"Not extracted {type(ins.called)} {ins}")
def can_be_low_level(ir: slither.slithir.operations.high_level_call.HighLevelCall) -> bool:
1224def can_be_low_level(ir: HighLevelCall) -> bool:
1225    return ir.function_name in [
1226        "transfer",
1227        "send",
1228        "call",
1229        "delegatecall",
1230        "callcode",
1231        "staticcall",
1232        "raw_call",
1233    ]
1236def convert_to_low_level(
1237    ir: HighLevelCall,
1238) -> Union[Send, LowLevelCall, Transfer,]:
1239    """
1240    Convert to a transfer/send/or low level call
1241    The funciton assume to receive a correct IR
1242    The checks must be done by the caller
1243
1244    Must be called after can_be_low_level
1245    """
1246    if ir.function_name == "transfer":
1247        assert len(ir.arguments) == 1
1248        prev_ir = ir
1249        ir = Transfer(ir.destination, ir.arguments[0])
1250        ir.set_expression(prev_ir.expression)
1251        ir.set_node(prev_ir.node)
1252        return ir
1253    if ir.function_name == "send":
1254        assert len(ir.arguments) == 1
1255        prev_ir = ir
1256        ir = Send(ir.destination, ir.arguments[0], ir.lvalue)
1257        ir.set_expression(prev_ir.expression)
1258        ir.set_node(prev_ir.node)
1259        ir.lvalue.set_type(ElementaryType("bool"))
1260        return ir
1261    if ir.function_name in ["call", "delegatecall", "callcode", "staticcall", "raw_call"]:
1262        new_ir = LowLevelCall(
1263            ir.destination, ir.function_name, ir.nbr_arguments, ir.lvalue, ir.type_call
1264        )
1265        new_ir.call_gas = ir.call_gas
1266        new_ir.call_value = ir.call_value
1267        new_ir.arguments = ir.arguments
1268        # TODO fix this for Vyper
1269        if ir.node.compilation_unit.solc_version >= "0.5":
1270            new_ir.lvalue.set_type([ElementaryType("bool"), ElementaryType("bytes")])
1271        else:
1272            new_ir.lvalue.set_type(ElementaryType("bool"))
1273        new_ir.set_expression(ir.expression)
1274        new_ir.set_node(ir.node)
1275        return new_ir
1276    raise SlithIRError(f"Incorrect conversion to low level {ir}")

Convert to a transfer/send/or low level call The funciton 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:
1279def can_be_solidity_func(ir: HighLevelCall) -> bool:
1280    if not isinstance(ir, HighLevelCall):
1281        return False
1282    return ir.destination.name == "abi" and ir.function_name in [
1283        "encode",
1284        "encodePacked",
1285        "encodeWithSelector",
1286        "encodeWithSignature",
1287        "encodeCall",
1288        "decode",
1289    ]
1292def convert_to_solidity_func(
1293    ir: HighLevelCall,
1294) -> SolidityCall:
1295    """
1296    Must be called after can_be_solidity_func
1297    :param ir:
1298    :return:
1299    """
1300    call = SolidityFunction(f"abi.{ir.function_name}()")
1301    new_ir = SolidityCall(call, ir.nbr_arguments, ir.lvalue, ir.type_call)
1302    new_ir.arguments = ir.arguments
1303    new_ir.set_expression(ir.expression)
1304    new_ir.set_node(ir.node)
1305    if isinstance(call.return_type, list) and len(call.return_type) == 1:
1306        new_ir.lvalue.set_type(call.return_type[0])
1307    elif (
1308        isinstance(new_ir.lvalue, TupleVariable)
1309        and call == SolidityFunction("abi.decode()")
1310        and len(new_ir.arguments) == 2
1311        and isinstance(new_ir.arguments[1], list)
1312    ):
1313        types = []
1314        for arg_type in new_ir.arguments[1]:
1315            decode_type = arg_type
1316            if isinstance(decode_type, (Structure, Enum, Contract)):
1317                decode_type = UserDefinedType(decode_type)
1318            types.append(decode_type)
1319        new_ir.lvalue.set_type(types)
1320    # abi.decode where the type to decode is a singleton
1321    # abi.decode(a, (uint))
1322    elif call == SolidityFunction("abi.decode()") and len(new_ir.arguments) == 2:
1323        # If the variable is a referenceVariable, we are lost
1324        # See https://github.com/crytic/slither/issues/566 for potential solutions
1325        if not isinstance(new_ir.arguments[1], ReferenceVariable):
1326            decode_type = new_ir.arguments[1]
1327            if isinstance(decode_type, (Structure, Enum, Contract)):
1328                decode_type = UserDefinedType(decode_type)
1329            new_ir.lvalue.set_type(decode_type)
1330    else:
1331        new_ir.lvalue.set_type(call.return_type)
1332    return new_ir

Must be called after can_be_solidity_func

Parameters
  • ir:
Returns
1335def convert_to_push_expand_arr(
1336    ir: HighLevelCall, node: "Node", ret: List[Any]
1337) -> TemporaryVariable:
1338    arr = ir.destination
1339
1340    length = ReferenceVariable(node)
1341    length.set_type(ElementaryType("uint256"))
1342
1343    ir_length = Length(arr, length)
1344    ir_length.set_expression(ir.expression)
1345    ir_length.set_node(ir.node)
1346    ir_length.lvalue.points_to = arr
1347    ret.append(ir_length)
1348
1349    length_val = TemporaryVariable(node)
1350    length_val.set_type(ElementaryType("uint256"))
1351    ir_get_length = Assignment(length_val, length, ElementaryType("uint256"))
1352    ir_get_length.set_expression(ir.expression)
1353    ir_get_length.set_node(ir.node)
1354    ret.append(ir_get_length)
1355
1356    new_length_val = TemporaryVariable(node)
1357    ir_add_1 = Binary(
1358        new_length_val, length_val, Constant("1", ElementaryType("uint256")), BinaryType.ADDITION
1359    )
1360    ir_add_1.set_expression(ir.expression)
1361    ir_add_1.set_node(ir.node)
1362    ret.append(ir_add_1)
1363
1364    ir_assign_length = Assignment(length, new_length_val, ElementaryType("uint256"))
1365    ir_assign_length.set_expression(ir.expression)
1366    ir_assign_length.set_node(ir.node)
1367    ret.append(ir_assign_length)
1368
1369    return length_val
1372def convert_to_push_set_val(
1373    ir: HighLevelCall,
1374    node: "Node",
1375    length_val: TemporaryVariable,
1376    ret: List[
1377        Union[
1378            Length,
1379            Assignment,
1380            Binary,
1381        ]
1382    ],
1383) -> None:
1384    arr = ir.destination
1385
1386    new_type = ir.destination.type.type
1387
1388    element_to_add = ReferenceVariable(node)
1389    element_to_add.set_type(new_type)
1390    ir_assign_element_to_add = Index(element_to_add, arr, length_val)
1391    ir_assign_element_to_add.set_expression(ir.expression)
1392    ir_assign_element_to_add.set_node(ir.node)
1393    ret.append(ir_assign_element_to_add)
1394
1395    if len(ir.arguments) > 0:
1396        assign_value = ir.arguments[0]
1397        if isinstance(assign_value, list):
1398            assign_value = TemporaryVariable(node)
1399            assign_value.set_type(element_to_add.type)
1400            ir_assign_value = InitArray(ir.arguments[0], assign_value)
1401            ir_assign_value.set_expression(ir.expression)
1402            ir_assign_value.set_node(ir.node)
1403            ret.append(ir_assign_value)
1404
1405        ir_assign_value = Assignment(element_to_add, assign_value, assign_value.type)
1406        ir_assign_value.set_expression(ir.expression)
1407        ir_assign_value.set_node(ir.node)
1408        ret.append(ir_assign_value)
1409    else:
1410        new_element = ir.lvalue
1411        new_element.set_type(new_type)
1412        ir_assign_value = Assignment(new_element, element_to_add, new_type)
1413        ir_assign_value.set_expression(ir.expression)
1414        ir_assign_value.set_node(ir.node)
1415        ret.append(ir_assign_value)
1418def convert_to_push(
1419    ir: HighLevelCall, node: "Node"
1420) -> List[Union[Length, Assignment, Binary, Index, InitArray,]]:
1421    """
1422    Convert a call to a series of operations to push a new value onto the array
1423
1424    The function assume to receive a correct IR
1425    The checks must be done by the caller
1426
1427    May necessitate to create an intermediate operation (InitArray)
1428    Necessitate to return the length (see push documentation)
1429    As a result, the function return may return a list
1430    """
1431
1432    ret = []
1433
1434    length_val = convert_to_push_expand_arr(ir, node, ret)
1435    convert_to_push_set_val(ir, node, length_val, ret)
1436
1437    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

1440def convert_to_pop(ir: HighLevelCall, node: "Node") -> List[Operation]:
1441    """
1442    Convert pop operators
1443    Return a list of 6 operations
1444    """
1445
1446    ret: List[Operation] = []
1447
1448    arr = ir.destination
1449    length = ReferenceVariable(node)
1450    length.set_type(ElementaryType("uint256"))
1451
1452    ir_length = Length(arr, length)
1453    assert ir.expression
1454    ir_length.set_expression(ir.expression)
1455    ir_length.set_node(ir.node)
1456    length.points_to = arr
1457    ret.append(ir_length)
1458
1459    val = TemporaryVariable(node)
1460
1461    ir_sub_1 = Binary(val, length, Constant("1", ElementaryType("uint256")), BinaryType.SUBTRACTION)
1462    ir_sub_1.set_expression(ir.expression)
1463    ir_sub_1.set_node(ir.node)
1464    ret.append(ir_sub_1)
1465
1466    element_to_delete = ReferenceVariable(node)
1467    ir_assign_element_to_delete = Index(element_to_delete, arr, val)
1468    # TODO the following is equivalent to length.points_to = arr
1469    # Should it be removed?
1470    ir_length.lvalue.points_to = arr
1471    # Note bytes is an ElementaryType not ArrayType and bytes1 should be returned
1472    # since bytes is bytes1[] without padding between the elements
1473    # while in other cases such as uint256[] (ArrayType) we use ir.destination.type.type
1474    # in this way we will have the type always set to the corresponding ElementaryType
1475    element_to_delete.set_type(
1476        ElementaryType("bytes1")
1477        if isinstance(ir.destination.type, ElementaryType)
1478        else ir.destination.type.type
1479    )
1480    ir_assign_element_to_delete.set_expression(ir.expression)
1481    ir_assign_element_to_delete.set_node(ir.node)
1482    ret.append(ir_assign_element_to_delete)
1483
1484    ir_delete = Delete(element_to_delete, element_to_delete)
1485    ir_delete.set_expression(ir.expression)
1486    ir_delete.set_node(ir.node)
1487    ret.append(ir_delete)
1488
1489    length_to_assign = ReferenceVariable(node)
1490    length_to_assign.set_type(ElementaryType("uint256"))
1491    ir_length = Length(arr, length_to_assign)
1492    ir_length.set_expression(ir.expression)
1493    length_to_assign.points_to = arr
1494    ir_length.set_node(ir.node)
1495    ret.append(ir_length)
1496
1497    ir_assign_length = Assignment(length_to_assign, val, ElementaryType("uint256"))
1498    ir_assign_length.set_expression(ir.expression)
1499    ir_assign_length.set_node(ir.node)
1500    ret.append(ir_assign_length)
1501
1502    return ret

Convert pop operators Return a list of 6 operations

1505def look_for_library_or_top_level(
1506    ir: HighLevelCall,
1507    using_for,
1508    t: Union[
1509        UserDefinedType,
1510        ElementaryType,
1511        str,
1512        TypeAliasTopLevel,
1513    ],
1514) -> Optional[Union[LibraryCall, InternalCall,]]:
1515    for destination in using_for[t]:
1516        if isinstance(destination, FunctionTopLevel) and destination.name == ir.function_name:
1517            arguments = [ir.destination] + ir.arguments
1518            if (
1519                len(destination.parameters) == len(arguments)
1520                and _find_function_from_parameter(arguments, [destination], True) is not None
1521            ):
1522                internalcall = InternalCall(destination, ir.nbr_arguments, ir.lvalue, ir.type_call)
1523                internalcall.set_expression(ir.expression)
1524                internalcall.set_node(ir.node)
1525                internalcall.arguments = [ir.destination] + ir.arguments
1526                return_type = internalcall.function.return_type
1527                if return_type:
1528                    if len(return_type) == 1:
1529                        internalcall.lvalue.set_type(return_type[0])
1530                    elif len(return_type) > 1:
1531                        internalcall.lvalue.set_type(return_type)
1532                else:
1533                    internalcall.lvalue = None
1534                return internalcall
1535
1536        lib_contract = None
1537        if isinstance(destination, FunctionContract) and destination.contract.is_library:
1538            lib_contract = destination.contract
1539        elif isinstance(destination, UserDefinedType) and isinstance(destination.type, Contract):
1540            lib_contract = destination.type
1541
1542        if lib_contract:
1543            lib_call = LibraryCall(
1544                lib_contract,
1545                ir.function_name,
1546                ir.nbr_arguments,
1547                ir.lvalue,
1548                ir.type_call,
1549                names=ir.names,
1550            )
1551            lib_call.set_expression(ir.expression)
1552            lib_call.set_node(ir.node)
1553            lib_call.call_gas = ir.call_gas
1554            lib_call.arguments = [ir.destination] + ir.arguments
1555            new_ir = convert_type_library_call(lib_call, lib_contract)
1556            if new_ir:
1557                new_ir.set_node(ir.node)
1558                return new_ir
1559    return None
1562def convert_to_library_or_top_level(
1563    ir: HighLevelCall, node: "Node", using_for
1564) -> Optional[Union[LibraryCall, InternalCall,]]:
1565    t = ir.destination.type
1566    if t in using_for:
1567        new_ir = look_for_library_or_top_level(ir, using_for, t)
1568        if new_ir:
1569            return new_ir
1570
1571    if "*" in using_for:
1572        new_ir = look_for_library_or_top_level(ir, using_for, "*")
1573        if new_ir:
1574            return new_ir
1575
1576    if (
1577        isinstance(t, ElementaryType)
1578        and t.name == "address"
1579        and ir.destination.name == "this"
1580        and UserDefinedType(node.function.contract) in using_for
1581    ):
1582        new_ir = look_for_library_or_top_level(
1583            ir, using_for, UserDefinedType(node.function.contract)
1584        )
1585        if new_ir:
1586            return new_ir
1587
1588    return None
1591def get_type(
1592    t: Union[
1593        UserDefinedType,
1594        ElementaryType,
1595    ]
1596) -> str:
1597    """
1598    Convert a type to a str
1599    If the instance is a Contract, return 'address' instead
1600    """
1601    if isinstance(t, UserDefinedType):
1602        if isinstance(t.type, Contract):
1603            return "address"
1604    return str(t)

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

1615def convert_type_library_call(ir: HighLevelCall, lib_contract: Contract) -> Optional[LibraryCall]:
1616    func = None
1617    candidates = [
1618        f
1619        for f in lib_contract.functions
1620        if f.name == ir.function_name
1621        and not f.is_shadowed
1622        and len(f.parameters) == len(ir.arguments)
1623    ]
1624
1625    if len(candidates) == 1:
1626        func = candidates[0]
1627    # We can discard if there are arguments here because libraries only support constant variables
1628    # And constant variables cannot have non-value type
1629    # i.e. "uint[2] constant arr = [1,2];" is not possible in Solidity
1630    # If this were to change, the following condition might be broken
1631    if func is None and not ir.arguments:
1632        # TODO: handle collision with multiple state variables/functions
1633        func = lib_contract.get_state_variable_from_name(ir.function_name)
1634    if func is None and candidates:
1635        func = _find_function_from_parameter(ir.arguments, candidates, False)
1636
1637    # In case of multiple binding to the same type
1638    # TODO: this part might not be needed with _find_function_from_parameter
1639    if not func:
1640        # specific lookup when the compiler does implicit conversion
1641        # for example
1642        # myFunc(uint)
1643        # can be called with an uint8
1644        for function in lib_contract.functions:
1645            if function.name == ir.function_name and len(function.parameters) == len(ir.arguments):
1646                func = function
1647                break
1648    if not func:
1649        return None
1650    ir.function = func
1651    if isinstance(func, Function):
1652        t = func.return_type
1653        # if its not a tuple, return a singleton
1654        if t and len(t) == 1:
1655            t = t[0]
1656    else:
1657        # otherwise its a variable (getter)
1658        t = func.type
1659    if t:
1660        ir.lvalue.set_type(t)
1661    else:
1662        ir.lvalue = None
1663    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]:
1707def convert_type_of_high_and_internal_level_call(
1708    ir: Operation, contract: Optional[Contract]
1709) -> Optional[Operation]:
1710    """
1711    Convert the IR type based on heuristic
1712
1713    Args:
1714        ir: target
1715        contract: optional contract. This should be the target of the IR. It will be used to look up potential functions
1716
1717    Returns:
1718        Potential new IR
1719    """
1720
1721    func = None
1722    if isinstance(ir, InternalCall):
1723        candidates: List[Function]
1724        if ir.function_candidates:
1725            # This path is taken only for SolidityImportPlaceHolder
1726            # Here we have already done a filtering on the potential targets
1727            candidates = ir.function_candidates
1728        else:
1729            candidates = [
1730                f
1731                for f in contract.functions
1732                if f.name == ir.function_name
1733                and f.contract_declarer.name == ir.contract_name
1734                and len(f.parameters) == len(ir.arguments)
1735            ]
1736
1737            for import_statement in contract.file_scope.imports:
1738                if (
1739                    import_statement.alias is not None
1740                    and import_statement.alias == ir.contract_name
1741                ):
1742                    imported_scope = contract.compilation_unit.get_scope(import_statement.filename)
1743                    candidates += [
1744                        f
1745                        for f in list(imported_scope.functions)
1746                        if f.name == ir.function_name and len(f.parameters) == len(ir.arguments)
1747                    ]
1748
1749        func = _find_function_from_parameter(ir.arguments, candidates, False)
1750
1751        if not func:
1752            assert contract
1753            func = contract.get_state_variable_from_name(ir.function_name)
1754    else:
1755        assert isinstance(ir, HighLevelCall)
1756        assert contract
1757        candidates = [
1758            f
1759            for f in contract.functions
1760            if f.name == ir.function_name
1761            and not f.is_shadowed
1762            and len(f.parameters) == len(ir.arguments)
1763        ]
1764        if len(candidates) == 1:
1765            func = candidates[0]
1766        if func is None:
1767            # TODO: handle collision with multiple state variables/functions
1768            func = contract.get_state_variable_from_name(ir.function_name)
1769        if func is None and candidates:
1770            func = _find_function_from_parameter(ir.arguments, candidates, False)
1771
1772    # low level lookup needs to be done as last step
1773    if not func:
1774        if can_be_low_level(ir):
1775            return convert_to_low_level(ir)
1776        if can_be_solidity_func(ir):
1777            return convert_to_solidity_func(ir)
1778    if not func:
1779        to_log = f"Function not found {ir.function_name}"
1780        logger.error(to_log)
1781    ir.function = func
1782    if isinstance(func, Function):
1783        return_type = func.return_type
1784        # if its not a tuple; return a singleton
1785        if return_type and len(return_type) == 1:
1786            return_type = return_type[0]
1787    else:
1788        # otherwise its a variable (getter)
1789        # If its a mapping or a array
1790        # we iterate until we find the final type
1791        # mapping and array can be mixed together
1792        # ex:
1793        #    mapping ( uint => mapping ( uint => uint)) my_var
1794        #    mapping(uint => uint)[] test;p
1795        if isinstance(func.type, (MappingType, ArrayType)):
1796            tmp = func.type
1797            while isinstance(tmp, (MappingType, ArrayType)):
1798                if isinstance(tmp, MappingType):
1799                    tmp = tmp.type_to
1800                else:
1801                    tmp = tmp.type
1802            return_type = tmp
1803        else:
1804            return_type = func.type
1805    if return_type:
1806
1807        # If the return type is a structure, but the lvalue is a tuple
1808        # We convert the type of the structure to a list of element
1809        # TODO: explore to replace all tuple variables by structures
1810        if (
1811            isinstance(ir.lvalue, TupleVariable)
1812            and isinstance(return_type, UserDefinedType)
1813            and isinstance(return_type.type, Structure)
1814        ):
1815            return_type = _convert_to_structure_to_list(return_type)
1816
1817        ir.lvalue.set_type(return_type)
1818    else:
1819        ir.lvalue = None
1820
1821    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:
1832def find_references_origin(irs: List[Operation]) -> None:
1833    """
1834    Make lvalue of each Index, Member operation
1835    points to the left variable
1836    """
1837    for ir in irs:
1838        if isinstance(ir, (Index, Member)):
1839            ir.lvalue.points_to = ir.variable_left

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

def remove_temporary(result):
1850def remove_temporary(result):
1851    result = [
1852        ins
1853        for ins in result
1854        if not isinstance(
1855            ins,
1856            (
1857                Argument,
1858                TmpNewElementaryType,
1859                TmpNewContract,
1860                TmpNewArray,
1861                TmpNewStructure,
1862            ),
1863        )
1864    ]
1865
1866    return result
1869def remove_unused(result: List[Operation]) -> List[Operation]:
1870    removed = True
1871
1872    if not result:
1873        return result
1874
1875    # dont remove the last elem, as it may be used by RETURN
1876    last_elem = result[-1]
1877
1878    while removed:
1879        removed = False
1880
1881        to_keep = []
1882        to_remove = []
1883
1884        # keep variables that are read
1885        # and reference that are written
1886        for ins in result:
1887            to_keep += [str(x) for x in ins.read]
1888            if isinstance(ins, OperationWithLValue) and not isinstance(ins, (Index, Member)):
1889                if isinstance(ins.lvalue, ReferenceVariable):
1890                    to_keep += [str(ins.lvalue)]
1891
1892        for ins in result:
1893            if isinstance(ins, Member):
1894                if not ins.lvalue.name in to_keep and ins != last_elem:
1895                    to_remove.append(ins)
1896                    removed = True
1897            # Remove type(X) if X is an elementary type
1898            # This assume that type(X) is only used with min/max
1899            # If Solidity introduces other operation, we might remove this removal
1900            if isinstance(ins, SolidityCall) and ins.function == SolidityFunction("type()"):
1901                if isinstance(ins.arguments[0], ElementaryType):
1902                    to_remove.append(ins)
1903
1904        result = [i for i in result if not i in to_remove]
1905    return result
def convert_constant_types(irs: List[slither.slithir.operations.operation.Operation]) -> None:
1916def convert_constant_types(irs: List[Operation]) -> None:
1917    """
1918    late conversion of uint -> type for constant (Literal)
1919    :param irs:
1920    :return:
1921    """
1922    # TODO: implement instances lookup for events, NewContract
1923    was_changed = True
1924    while was_changed:
1925        was_changed = False
1926        for ir in irs:
1927            if isinstance(ir, (Assignment, Binary)):
1928                if (
1929                    isinstance(ir.lvalue.type, ElementaryType)
1930                    and ir.lvalue.type.type in ElementaryTypeInt
1931                ):
1932                    for r in ir.read:
1933                        if isinstance(r, Constant) and r.type.type not in ElementaryTypeInt:
1934                            r.set_type(ElementaryType(ir.lvalue.type.type))
1935                            was_changed = True
1936
1937            if isinstance(ir, (HighLevelCall, InternalCall)):
1938                func = ir.function
1939                if isinstance(func, StateVariable):
1940                    types = export_nested_types_from_variable(func)
1941                else:
1942                    types = [p.type for p in func.parameters]
1943                assert len(types) == len(ir.arguments)
1944                for idx, arg in enumerate(ir.arguments):
1945                    t = types[idx]
1946                    if isinstance(t, ElementaryType):
1947                        if t.type in ElementaryTypeInt:
1948                            if arg.type.type not in ElementaryTypeInt:
1949                                arg.set_type(ElementaryType(t.type))
1950                                was_changed = True
1951
1952            if isinstance(ir, NewStructure):
1953                st = ir.structure
1954                for idx, arg in enumerate(ir.arguments):
1955                    e = st.elems_ordered[idx]
1956                    if isinstance(e.type, ElementaryType):
1957                        if e.type.type in ElementaryTypeInt:
1958                            if arg.type.type not in ElementaryTypeInt:
1959                                arg.set_type(ElementaryType(e.type.type))
1960                                was_changed = True
1961
1962            def is_elementary_array(t):
1963                return isinstance(t, ArrayType) and isinstance(t.type, ElementaryType)
1964
1965            if isinstance(ir, InitArray):
1966                if is_elementary_array(ir.lvalue.type):
1967                    if ir.lvalue.type.type.type in ElementaryTypeInt:
1968                        for r in ir.read:
1969                            if isinstance(r, Constant) and is_elementary_array(r.type):
1970                                if r.type.type.type not in ElementaryTypeInt:
1971                                    r.set_type(ElementaryType(ir.lvalue.type.type.type))
1972                                    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:
1983def convert_delete(irs: List[Operation]) -> None:
1984    """
1985    Convert the lvalue of the Delete to point to the variable removed
1986    This can only be done after find_references_origin is called
1987    :param irs:
1988    :return:
1989    """
1990    for ir in irs:
1991        if isinstance(ir, Delete):
1992            if isinstance(ir.lvalue, ReferenceVariable):
1993                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]:
2022def apply_ir_heuristics(irs: List[Operation], node: "Node", is_solidity: bool) -> List[Operation]:
2023    """
2024    Apply a set of heuristic to improve slithIR
2025    """
2026
2027    irs = integrate_value_gas(irs)
2028
2029    irs = propagate_type_and_convert_call(irs, node)
2030    irs = remove_unused(irs)
2031    find_references_origin(irs)
2032
2033    # These are heuristics that are only applied to Solidity
2034    if is_solidity:
2035        convert_constant_types(irs)
2036        convert_delete(irs)
2037
2038    _find_source_mapping_references(irs)
2039
2040    return irs

Apply a set of heuristic to improve slithIR