contract ConfirmedTransactionModule is Module {

    string public constant NAME = "Confirmed Transaction Module";
    string public constant VERSION = "0.1.0";

    event Confirmed(
        bytes32 indexed transactionHash,
        address indexed manager
    );
    event Revoked(
        bytes32 indexed transactionHash,
        address indexed manager
    );
    event Executed(
        bytes32 indexed transactionHash,
        address indexed executor
    );
    event ExecutorUpdated(
        address indexed executor,
        bool allowed
    );


    enum TransactionStatus {
        Unknown,
        Confirmed,
        Executed
    }

    // transactionStatus maps transactionsHashes to their status
    mapping (bytes32 => TransactionStatus) private transactionStatus;

    // isExecutor mapping maps executor's address to executor status.
    mapping (address => bool) public isExecutor;

    modifier onlyExecutor() {
        require(isExecutor[msg.sender], "only executor can call");
        _;
    }

    /// @dev Setup function sets manager
    function setup()
        public
    {
        setManager();
    }

    /// @dev Adds an address to the executor allowlist. This can only be done via a Safe transaction.
    /// @param executor Executor address.
    /// @param allowed true if executor should be allowed, false otherwise.
    function setExecutor(address executor, bool allowed)
        public
        authorized
    {
        if (isExecutor[executor] != allowed) {
            isExecutor[executor] = allowed;
            emit ExecutorUpdated(executor, allowed);
        }
    }

    /// @dev Confirms a transaction to make it executable. This can only be done via a Safe transaction.
    /// @param transactionHash Transaction hash to be confirmed.
    function confirmTransaction(bytes32 transactionHash)
        public
        authorized
    {
        require(transactionStatus[transactionHash] == TransactionStatus.Unknown, "tx already confirmed");
        transactionStatus[transactionHash] = TransactionStatus.Confirmed;
        emit Confirmed(transactionHash, msg.sender);
    }

    /// @dev Revokes a previous confirmation. This can only be done via a Safe transaction.
    /// @param transactionHash Transaction hash to be revoked.
    function revokeTransaction(bytes32 transactionHash)
        public
        authorized
    {
        require(transactionStatus[transactionHash] == TransactionStatus.Confirmed, "tx unknown or already executed");
        transactionStatus[transactionHash] = TransactionStatus.Unknown;
        emit Revoked(transactionHash, msg.sender);
    }

    /// @dev Executes a confirmed transaction. Only designated executors can execute.
    /// @param to Destination address.
    /// @param value Ether value.
    /// @param data Data payload.
    /// @param operation Operation type.
    /// @param witness Witness for hash commitment.
    function executeTransaction(
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation,
        bytes32 witness
    )
        public
        onlyExecutor
    {
        bytes32 transactionHash = getTransactionHash(to, value, data, operation, witness);
        require(transactionStatus[transactionHash] == TransactionStatus.Confirmed, "tx unknown or already executed");
        transactionStatus[transactionHash] = TransactionStatus.Executed;
        require(manager.execTransactionFromModule(to, value, data, operation), "execution failed");
        emit Executed(transactionHash, msg.sender);
    }

    /// @dev Gets the status of a transaction.
    /// @param transactionHash Hash of transaction whose status is queried.
    /// @return A tuple of bools (confirmed, executed) indicating whether the
    /// transactions has been confirmed and executed.
    function getTransactionStatus(
        bytes32 transactionHash
    )
        public
        view
        returns (bool confirmed, bool executed)
    {
        TransactionStatus status = transactionStatus[transactionHash];
        if (status == TransactionStatus.Unknown) {
            return (false, false);
        } else if (status == TransactionStatus.Confirmed) {
            return (true, false);
        } else if (status == TransactionStatus.Executed) {
            return (true, true);
        } else {
            // We should never reach this case
            assert(false);
        }
    }

    /// @dev Returns hash to be signed by owners.
    /// @param to Destination address.
    /// @param value Ether value.
    /// @param data Data payload.
    /// @param operation Operation type.
    /// @param witness Witness for hash commitment.
    /// @return Transaction hash.
    function getTransactionHash(
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation,
        bytes32 witness
    )
        public
        pure
        returns (bytes32)
    {
        require(operation == Enum.Operation.Call || operation == Enum.Operation.DelegateCall, "unknown operation");
        return keccak256(abi.encode(to, value, data, operation, witness));
    }
}
/// @title Confirmed Transaction Module - Enables the Safe to designate transactions that can be executed by an executor at any time. The set of executors is managed by the Safe.
function getTransactionHash(
    address to,
    uint256 value,
    bytes memory data,
    Enum.Operation operation,
    bytes32 witness
)
    public
    pure
    returns (bytes32)
{
    require(operation == Enum.Operation.Call || operation == Enum.Operation.DelegateCall, "unknown operation");
    return keccak256(abi.encode(to, value, data, operation, witness));
}

/// @dev Returns hash to be signed by owners.
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param witness Witness for hash commitment.
/// @return Transaction hash.