contract PartiallyDelayedMultiSig is
    DelayedMultiSig
{
    // ============ Events ============

    event SelectorSet(address destination, bytes4 selector, bool approved);

    // ============ Constants ============

    bytes4 constant internal BYTES_ZERO = bytes4(0x0);

    // ============ Storage ============

    // destination => function selector => can bypass timelock
    mapping (address => mapping (bytes4 => bool)) public instantData;

    // ============ Modifiers ============

    // Overrides old modifier that requires a timelock for every transaction
    modifier pastTimeLock(
        uint256 transactionId
    ) {
        // if the function selector is not exempt from timelock, then require timelock
        require(
            block.timestamp >= confirmationTimes[transactionId] + secondsTimeLocked
            || txCanBeExecutedInstantly(transactionId),
            "TIME_LOCK_INCOMPLETE"
        );
        _;
    }

    // ============ Constructor ============

    /**
     * Contract constructor sets initial owners, required number of confirmations, and time lock.
     *
     * @param  _owners               List of initial owners.
     * @param  _required             Number of required confirmations.
     * @param  _secondsTimeLocked    Duration needed after a transaction is confirmed and before it
     *                               becomes executable, in seconds.
     * @param  _noDelayDestinations  List of destinations that correspond with the selectors.
     *                               Zero address allows the function selector to be used with any
     *                               address.
     * @param  _noDelaySelectors     All function selectors that do not require a delay to execute.
     *                               Fallback function is 0x00000000.
     */
    constructor (
        address[] memory _owners,
        uint256 _required,
        uint32 _secondsTimeLocked,
        address[] memory _noDelayDestinations,
        bytes4[] memory _noDelaySelectors
    )
        public
        DelayedMultiSig(_owners, _required, _secondsTimeLocked)
    {
        require(
            _noDelayDestinations.length == _noDelaySelectors.length,
            "ADDRESS_AND_SELECTOR_MISMATCH"
        );

        for (uint256 i = 0; i < _noDelaySelectors.length; i++) {
            address destination = _noDelayDestinations[i];
            bytes4 selector = _noDelaySelectors[i];
            instantData[destination][selector] = true;
            emit SelectorSet(destination, selector, true);
        }
    }

    // ============ Wallet-Only Functions ============

    /**
     * Adds or removes functions that can be executed instantly. Transaction must be sent by wallet.
     *
     * @param  destination  Destination address of function. Zero address allows the function to be
     *                      sent to any address.
     * @param  selector     4-byte selector of the function. Fallback function is 0x00000000.
     * @param  approved     True if adding approval, false if removing approval.
     */
    function setSelector(
        address destination,
        bytes4 selector,
        bool approved
    )
        public
        onlyWallet
    {
        instantData[destination][selector] = approved;
        emit SelectorSet(destination, selector, approved);
    }

    // ============ Helper Functions ============

    /**
     * Returns true if transaction can be executed instantly (without timelock).
     */
    function txCanBeExecutedInstantly(
        uint256 transactionId
    )
        internal
        view
        returns (bool)
    {
        // get transaction from storage
        Transaction memory txn = transactions[transactionId];
        address dest = txn.destination;
        bytes memory data = txn.data;

        // fallback function
        if (data.length == 0) {
            return selectorCanBeExecutedInstantly(dest, BYTES_ZERO);
        }

        // invalid function selector
        if (data.length < 4) {
            return false;
        }

        // check first four bytes (function selector)
        bytes32 rawData;
        /* solium-disable-next-line security/no-inline-assembly */
        assembly {
            rawData := mload(add(data, 32))
        }
        bytes4 selector = bytes4(rawData);

        return selectorCanBeExecutedInstantly(dest, selector);
    }

    /**
     * Function selector is in instantData for address dest (or for address zero).
     */
    function selectorCanBeExecutedInstantly(
        address destination,
        bytes4 selector
    )
        internal
        view
        returns (bool)
    {
        return instantData[destination][selector]
            || instantData[ADDRESS_ZERO][selector];
    }
}
/**
 * @title PartiallyDelayedMultiSig
 * @author dYdX
 *
 * Multi-Signature Wallet with delay in execution except for some function selectors.
 */
function txCanBeExecutedInstantly(
    uint256 transactionId
)
    internal
    view
    returns (bool)
{
    // get transaction from storage
    Transaction memory txn = transactions[transactionId];
    address dest = txn.destination;
    bytes memory data = txn.data;

    // fallback function
    if (data.length == 0) {
        return selectorCanBeExecutedInstantly(dest, BYTES_ZERO);
    }

    // invalid function selector
    if (data.length < 4) {
        return false;
    }

    // check first four bytes (function selector)
    bytes32 rawData;
    /* solium-disable-next-line security/no-inline-assembly */
    assembly {
        rawData := mload(add(data, 32))
    }
    bytes4 selector = bytes4(rawData);

    return selectorCanBeExecutedInstantly(dest, selector);
}

/**
 * Returns true if transaction can be executed instantly (without timelock).
 */