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 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 Revokes a previous confirmation. This can only be done via a Safe transaction.
/// @param transactionHash Transaction hash to be revoked.