"""This module contains the detection code to find multiple sends occurring in
a single transaction."""
from copy import copy
from typing import cast, List, Optional

from mythril.analysis.ops import Call
from mythril.analysis.report import Issue
from mythril.analysis.swc_data import MULTIPLE_SENDS
from mythril.analysis.modules.base import DetectionModule
from mythril.laser.ethereum.state.annotation import StateAnnotation
from mythril.laser.ethereum.state.global_state import GlobalState
import logging
from mythril.analysis.call_helpers import get_call_from_state

log = logging.getLogger(__name__)


class MultipleSendsAnnotation(StateAnnotation):
    def __init__(self) -> None:
        self.calls = []  # type: List[Optional[Call]]

    def __copy__(self):
        result = MultipleSendsAnnotation()
        result.calls = copy(self.calls)
        return result


class MultipleSendsModule(DetectionModule):
    """This module checks for multiple sends in a single transaction."""

    def __init__(self):
        """"""
        super().__init__(
            name="Multiple Sends",
            swc_id=MULTIPLE_SENDS,
            description="Check for multiple sends in a single transaction",
            entrypoint="callback",
            pre_hooks=[
                "CALL",
                "DELEGATECALL",
                "STATICCALL",
                "CALLCODE",
                "RETURN",
                "STOP",
            ],
        )

    def execute(self, state: GlobalState):
        self._issues.extend(_analyze_state(state))
        return self.issues


def _analyze_state(state: GlobalState):
    """
    :param state: the current state
    :return: returns the issues for that corresponding state
    """
    instruction = state.get_current_instruction()

    annotations = cast(
        List[MultipleSendsAnnotation],
        list(state.get_annotations(MultipleSendsAnnotation)),
    )
    if len(annotations) == 0:
        log.debug("Creating annotation for state")
        state.annotate(MultipleSendsAnnotation())
        annotations = cast(
            List[MultipleSendsAnnotation],
            list(state.get_annotations(MultipleSendsAnnotation)),
        )

    calls = annotations[0].calls

    if instruction["opcode"] in ["CALL", "DELEGATECALL", "STATICCALL", "CALLCODE"]:
        call = get_call_from_state(state)
        if call:
            calls += [call]

    else:  # RETURN or STOP
        if len(calls) > 1:

            description_tail = (
                "Consecutive calls are executed at the following bytecode offsets:\n"
            )

            for call in calls:
                description_tail += "Offset: {}\n".format(
                    call.state.get_current_instruction()["address"]
                )

            description_tail += (
                "Try to isolate each external call into its own transaction,"
                " as external calls can fail accidentally or deliberately.\n"
            )

            issue = Issue(
                contract=state.environment.active_account.contract_name,
                function_name=state.environment.active_function_name,
                address=instruction["address"],
                swc_id=MULTIPLE_SENDS,
                bytecode=state.environment.code.bytecode,
                title="Multiple Calls in a Single Transaction",
                severity="Medium",
                description_head="Multiple sends are executed in one transaction.",
                description_tail=description_tail,
                gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used),
            )

            return [issue]

    return []


detector = MultipleSendsModule()

@@ -692,24 +692,31 @@
    self.call
+_offset
 s = %5B%5D  # ty
@@ -728,22 +728,11 @@
 ist%5B
-Optional%5BCall%5D
+int
 %5D%0A%0A 
@@ -812,24 +812,31 @@
  result.call
+_offset
 s = copy(sel
@@ -841,16 +841,23 @@
 elf.call
+_offset
 s)%0A     
@@ -1945,59 +1945,8 @@
  0:%0A
-        log.debug(%22Creating annotation for state%22)%0A
     
@@ -2147,16 +2147,23 @@
     call
+_offset
 s = anno
@@ -2177,16 +2177,23 @@
 %5B0%5D.call
+_offset
 s%0A%0A    i
@@ -2287,82 +2287,67 @@
 call
- = get_call_from_state(state)%0A        if call:%0A            calls += %5Bcall%5D
+_offsets.append(state.get_current_instruction()%5B%22address%22%5D)
 %0A%0A  
@@ -2391,16 +2391,23 @@
 len(call
+_offset
 s) %3E 1:%0A
@@ -2423,160 +2423,40 @@
     
-description_tail = (%0A                %22Consecutive calls are executed at the following bytecode offsets:%5Cn%22%0A            )%0A%0A            for call in calls:
+for offset in call_offsets%5B1:%5D:%0A
 %0A   
@@ -2489,32 +2489,10 @@
 ail 
-+
 = 
-%22Offset: %7B%7D%5Cn%22.format
 (%0A  
@@ -2513,109 +2513,84 @@
     
-call.state.get_current_instruction()%5B%22address%22%5D%0A                )%0A%0A            description_tail += (%0A
+%22This call is executed after a previous call in the same transaction. %22%0A    
     
@@ -2622,29 +2622,38 @@
 te each 
-external call
+call, transfer or send
  into it
@@ -2669,17 +2669,17 @@
 nsaction
-,
+.
 %22%0A      
@@ -2692,85 +2692,15 @@
     
-%22 as external calls can fail accidentally or deliberately.%5Cn%22%0A            )%0A%0A
+)%0A%0A    
     
@@ -2722,16 +2722,20 @@
  Issue(%0A
+    
         
@@ -2815,16 +2815,20 @@
         
+   
+ 
 function
@@ -2893,16 +2893,20 @@
     
+    
 address=
 inst
@@ -2905,32 +2905,20 @@
 ess=
-instruction%5B%22address%22%5D,%0A
+offset,%0A    
     
@@ -2944,32 +2944,36 @@
 MULTIPLE_SENDS,%0A
+    
                 
@@ -3030,16 +3030,20 @@
         
+    
 title=%22M
@@ -3098,16 +3098,20 @@
         
+    
 severity
@@ -3116,14 +3116,11 @@
 ty=%22
-Medium
+Low
 %22,%0A 
@@ -3126,32 +3126,36 @@
                 
+ 
+   
 description_head
@@ -3165,20 +3165,20 @@
 ultiple 
-send
+call
 s are ex
@@ -3187,18 +3187,23 @@
 uted in 
-on
+the sam
 e transa
@@ -3211,16 +3211,20 @@
 tion.%22,%0A
+    
         
@@ -3266,16 +3266,20 @@
 n_tail,%0A
+    
         
@@ -3355,35 +3355,43 @@
 d),%0A            
-)%0A%0A
+    )%0A%0A    
             retu

Refactor multiple_sends module