// Contract: DSLOProtocol
// Target function: function cancelOrder(Order calldata order) public
 role.
   */
  modifier onlyGuardian() {
    require(guardians[msg.sender], 'KyberSwapRole: not guardian');
    _;
  }

  /**
   * @dev Update Operator role for user.
   * Can only be called by the current owner.
   */
  function updateOperator(address user, bool grantOrRevoke) external onlyOwner {
    operators[user] = grantOrRevoke;
    emit UpdateOperator(user, grantOrRevoke);
  }

  /**
   * @dev Update Guardian role for user.
   * Can only be called by the current owner.
   */
  function updateGuardian(address user, bool grantOrRevoke) external onlyOwner {
    guardians[user] = grantOrRevoke;
    emit UpdateGuardian(user, grantOrRevoke);
  }

  /**
   * @dev Enable logic for contract.
   * Can only be called by the current owner.
   */
  function enableLogic() external onlyOwner {
    _unpause();
  }

  /**
   * @dev Disable logic for contract.
   * Can only be called by the guardians.
   */
  function disableLogic() external onlyGuardian {
    _pause();
  }
}

/*
“Copyright (c) 2019-2021 1inch 
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: 
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE”.
*/

/// @title A helper contract for calculations related to order amounts
contract AmountCalculator {
  using Address for address;

  /// @notice Calculates maker amount
  /// @return Result Floored maker amount
  function getMakerAmount(
    uint256 orderMakerAmount,
    uint256 orderTakerAmount,
    uint256 swapTakerAmount
  ) public pure returns (uint256) {
    return (swapTakerAmount * orderMakerAmount) / orderTakerAmount;
  }

  /// @notice Calculates taker amount
  /// @return Result Ceiled taker amount
  function getTakerAmount(
    uint256 orderMakerAmount,
    uint256 orderTakerAmount,
    uint256 swapMakerAmount
  ) public pure returns (uint256) {
    return (swapMakerAmount * orderTakerAmount + orderMakerAmount - 1) / orderMakerAmount;
  }

  /// @notice Performs an arbitrary call to target with data
  /// @return Result Bytes transmuted to uint256
  function arbitraryStaticCall(address target, bytes memory data) external view returns (uint256) {
    bytes memory result = target.functionStaticCall(data, 'AC: arbitraryStaticCall');
    return abi.decode(result, (uint256));
  }
}

/*
“Copyright (c) 2019-2021 1inch 
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: 
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE”.
*/

/// @title A helper contract for managing nonce of tx sender
contract NonceManager {
  event NonceIncreased(address indexed maker, uint256 oldNonce, uint256 newNonce);

  mapping(address => uint256) public nonce;

  /// @notice Advances nonce by one
  function increaseNonce() external {
    advanceNonce(1);
  }

  /// @notice Advances nonce by specified amount
  function advanceNonce(uint8 amount) public {
    uint256 newNonce = nonce[msg.sender] + amount;
    nonce[msg.sender] = newNonce;
    emit NonceIncreased(msg.sender, newNonce - amount, newNonce);
  }

  /// @notice Checks if `makerAddress` has specified `makerNonce`
  /// @return Result True if `makerAddress` has specified nonce. Otherwise, false
  function nonceEquals(address makerAddress, uint256 makerNonce) external view returns (bool) {
    return nonce[makerAddress] == makerNonce;
  }
}

/*
“Copyright (c) 2019-2021 1inch 
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: 
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE”.
*/

/// @title A helper contract for executing boolean functions on arbitrary target call results
contract PredicateHelper {
  using Address for address;

  /// @notice Calls every target with corresponding data
  /// @return Result True if call to any target returned True. Otherwise, false
  function or(address[] calldata targets, bytes[] calldata data) external view returns (bool) {
    require(targets.length == data.length, 'PH: input array size mismatch');
    for (uint256 i = 0; i < targets.length; i++) {
      bytes memory result = targets[i].functionStaticCall(data[i], "PH: 'or' subcall failed");
      require(result.length == 32, 'PH: invalid call result');
      if (abi.decode(result, (bool))) {
        return true;
      }
    }
    return false;
  }

  /// @notice Calls every target with corresponding data
  /// @return Result True if calls to all targets returned True. Otherwise, false
  function and(address[] calldata targets, bytes[] calldata data) external view returns (bool) {
    require(targets.length == data.length, 'PH: input array size mismatch');
    for (uint256 i = 0; i < targets.length; i++) {
      bytes memory result = targets[i].functionStaticCall(data[i], "PH: 'and' subcall failed");
      require(result.length == 32, 'PH: invalid call result');
      if (!abi.decode(result, (bool))) {
        return false;
      }
    }
    return true;
  }

  /// @notice Calls target with specified data and tests if it's equal to the value
  /// @param value Value to test
  /// @return Result True if call to target returns the same value as `value`. Otherwise, false
  function eq(uint256 value, address target, bytes memory data) external view returns (bool) {
    bytes memory result = target.functionStaticCall(data, 'PH: eq');
    require(result.length == 32, 'PH: invalid call result');
    return abi.decode(result, (uint256)) == value;
  }

  /// @notice Calls target with specified data and tests if it's lower than value
  /// @param value Value to test
  /// @return Result True if call to target returns value which is lower than `value`. Otherwise, false
  function lt(uint256 value, address target, bytes memory data) external view returns (bool) {
    bytes memory result = target.functionStaticCall(data, 'PH: lt');
    require(result.length == 32, 'PH: invalid call result');
    return abi.decode(result, (uint256)) < value;
  }

  /// @notice Calls target with specified data and tests if it's bigger than value
  /// @param value Value to test
  /// @return Result True if call to target returns value which is bigger than `value`. Otherwise, false
  function gt(uint256 value, address target, bytes memory data) external view returns (bool) {
    bytes memory result = target.functionStaticCall(data, 'PH: gt');
    require(result.length == 32, 'PH: invalid call result');
    return abi.decode(result, (uint256)) > value;
  }

  /// @notice Checks passed time against block timestamp
  /// @return Result True if current block timestamp is lower than `time`. Otherwise, false
  function timestampBelow(uint256 time) external view returns (bool) {
    return block.timestamp < time; // solhint-disable-line not-rely-on-time
  }
}

/// @title Interface for interactor which acts between `maker => taker` and `taker => maker` transfers.
interface InteractiveNotificationReceiver {
  /// @notice Callback method that gets called after taker transferred funds to maker but before
  /// the opposite transfer happened
  function notifyFillOrder(
    address taker,
    address makerAsset,
    address takerAsset,
    uint256 makingAmount,
    uint256 takingAmount,
    bytes memory interactiveData
  ) external;
}

/// @title Interface for DAI-style permits
interface ILimitOrderCallee {
  function limitOrderCall(
    uint256 makingAmount,
    uint256 takingAmount,
    bytes memory callbackData
  ) external;
}

/// @title Interface for Double signature order
interface IDSOrderMixin {
  /// @notice Emitted every time order gets filled, including partial fills
  event OrderFilled(
    address indexed taker,
    bytes32 indexed orderHash,
    uint256 remaining,
    uint256 makingAmount,
    uint256 takingAmount
  );

  /// @notice Emitted when order gets cancelled
  event OrderCanceled(address indexed maker, bytes32 orderHash, uint256 remainingRaw);

  /// @notice Emitted when update interaction target whitelist
  event UpdatedInteractionWhitelist(address _address, bool isWhitelist);

  /// @notice Emitted when update ds order signer
  event UpdatedDSOrderSigner(address _address);

  /// @notice Emitted when collect fee
  event FeeCollected(address indexed recipient, address indexed token, uint256 amount);

  // address feeRecipient;
  //   uint32 amountTokenFeePercent;
  //   bool isTakerAssetFee;

  // Fixed-size order part with core information
  // `feeConfig`
  //      feeRecipient = address(uint160(params.order.feeConfig))
  //      amountTokenFeePercent = uint32(params.order.feeConfig >> 160)
  //      isTakerAssetFee = (params.order.feeConfig >> 192) != 0
  struct StaticOrder {
    uint256 salt;
    address makerAsset;
    address takerAsset;
    address maker;
    address receiver;
    address allowedSender; // equals to Zero address on public orders
    uint256 makingAmount;
    uint256 takingAmount;
    uint256 feeConfig; // bit slot  1 -> 32 -> 160: isTakerAssetFee - amountTokenFeePercent - feeRecipient
  }

  // `StaticOrder` extension including variable-sized additional order meta information
  // `feeConfig`
  //      feeRecipient = address(uint160(params.order.feeConfig))
  //      amountTokenFeePercent = uint32(params.order.feeConfig >> 160)
  //      isTakerAssetFee = (params.order.feeConfig >> 192) != 0
  struct Order {
    uint256 salt;
    address makerAsset;
    address takerAsset;
    address maker;
    address receiver;
    address allowedSender; // equals to Zero address on public orders
    uint256 makingAmount;
    uint256 takingAmount;
    uint256 feeConfig; // bit slot  1 -> 32 -> 160: isTakerAssetFee - amountTokenFeePercent - feeRecipient
    bytes makerAssetData;
    bytes takerAssetData;
    bytes getMakerAmount; // this.staticcall(abi.encodePacked(bytes, swapTakerAmount)) => (swapMakerAmount)
    bytes getTakerAmount; // this.staticcall(abi.encodePacked(bytes, swapMakerAmount)) => (swapTakerAmount)
    bytes predicate; // this.staticcall(bytes) => (bool)
    bytes interaction;
  }

  struct DSOrder {
    bytes32 orderHash;
    uint32 opExpireTime;
  }

  struct Signature {
    bytes orderSignature; // Signature to confirm quote ownership
    bytes opSignature; // OP Signature to confirm quote ownership
  }

  struct AmountData {
    uint256 makingAmount;
    uint256 takingAmount;
    uint256 thresholdAmount;
  }

  struct FillOrderParams {
    Order order;
    Signature signature;
    uint32 opExpireTime;
    address target;
    bytes callbackData;
  }

  struct FillBatchOrdersParams {
    Order[] orders;
    Signature[] signatures;
    uint32[] opExpireTimes;
    uint256 takingAmount;
    uint256 thresholdAmount;
    address target;
  }
}

/*
“Copyright (c) 2019-2021 1inch 
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: 
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software. 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE”.
*/

/// @title Library with gas efficient alternatives to `abi.decode`
library ArgumentsDecoder {
  function decodeUint256(bytes memory data) internal pure returns (uint256) {
    uint256 value;
    assembly {
      // solhint-disable-line no-inline-assembly
      value := mload(add(data, 0x20))
    }
    return value;
  }

  function decodeBool(bytes memory data) internal pure returns (bool) {
    bool value;
    assembly {
      // solhint-disable-line no-inline-assembly
      value := eq(mload(add(data, 0x20)), 1)
    }
    return value;
  }

  function decodeTargetAndCalldata(bytes memory data) internal pure returns (address, bytes memory) {
    address target;
    bytes memory args;
    assembly {
      // solhint-disable-line no-inline-assembly
      target := mload(add(data, 0x14))
      args := add(data, 0x14)
      mstore(args, sub(mload(data), 0x14))
    }
    return (target, args);
  }

  function decodeTargetAndData(bytes calldata data) internal pure returns (address, bytes calldata) {
    address target;
    bytes calldata args;
    assembly {
      // solhint-disable-line no-inline-assembly
      target := shr(96, calldataload(data.offset))
    }
    args = data[20:];
    return (target, args);
  }
}

/// @title Regular Limit Order mixin
abstract contract DSOrderMixin is
  EIP712,
  AmountCalculator,
  NonceManager,
  PredicateHelper,
  KyberSwapRole,
  IDSOrderMixin
{
  using Address for address;
  using ArgumentsDecoder for bytes;

  bytes32 public constant LIMIT_ORDER_TYPEHASH = keccak256(
    'Order(uint256 salt,address makerAsset,address takerAsset,address maker,address receiver,address allowedSender,uint256 makingAmount,uint256 takingAmount,uint256 feeConfig,bytes makerAssetData,bytes takerAssetData,bytes getMakerAmount,bytes getTakerAmount,bytes predicate,bytes interaction)'
  );

  bytes32 public constant DS_LIMIT_ORDER_TYPEHASH =
    keccak256('DSOrder(bytes32 orderHash,uint32 opExpireTime)');

  uint256 private constant _ORDER_DOES_NOT_EXIST = 0;
  uint256 private constant _ORDER_FILLED = 1;
  uint24 internal constant FEE_UNITS = 100_000;
  address private _dsOrderSigner;

  /// @notice Stores unfilled amounts for each order plus one
  /// Therefore 0 means order doesn't exist and 1 means order was filled
  mapping(bytes32 => uint256) private _remaining;
  mapping(address => bool) interactionWhitelist;

  /// @notice Update interaction target whitelist
  function updateInteractionWhitelist(address _address, bool isWhitelist) external onlyOwner {
    interactionWhitelist[_address] = isWhitelist;
    emit UpdatedInteractionWhitelist(_address, isWhitelist);
  }

  /// @notice Update dsOrderSigner
  function updateDSOrderSigner(address _address) external onlyOwner {
    _dsOrderSigner = _address;
    emit UpdatedDSOrderSigner(_address);
  }