creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/RelayerV2.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\n// Core\nimport {QuarantineList} from \"./core/QuarantineList.sol\";\n\n// Structs\nimport {UserRequest, RequestData, ProcessParam, AssetInfo} from \"./core/Structs.sol\";\n\n// Utils\nimport {Ownable} from \"./utils/Ownable.sol\";\nimport {PercentageMath} from \"./utils/PercentageMath.sol\";\nimport {SafeERC20} from \"openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol\";\n\n// Interfaces\nimport {IERC20} from \"openzeppelin-contracts/token/ERC20/IERC20.sol\";\nimport {IFyde} from \"./interfaces/IFyde.sol\";\nimport {IGovernanceModule} from \"./interfaces/IGovernanceModule.sol\";\nimport {IOracle} from \"./interfaces/IOracle.sol\";\nimport {ITaxModule} from \"./interfaces/ITaxModule.sol\";\n\n///@title RelayerV2\n///@notice The relayer is the entry point contract for users to interact with the protocol.\n///        The relayer is monitored by an off-chain keeper that will update the protocol AUM.\n\ncontract RelayerV2 is QuarantineList {\n  using SafeERC20 for IERC20;\n  /*//////////////////////////////////////////////////////////////\n                                 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n  ///@notice Fyde contract\n  IFyde public fyde;\n\n  ///@notice OracleModule contract\n  IOracle public oracleModule;\n\n  //@notice GovernanceModule contract\n  IGovernanceModule public immutable GOVERNANCE_MODULE;\n\n  //@notice calculates the tax for protocol actions\n  ITaxModule public taxModule;\n\n  ///@dev Only used for tracking events offchain\n  uint32 public nonce;\n\n  ///@notice Threshold of deviation for updating AUM\n  uint16 public deviationThreshold;\n\n  ///@notice State of the protocol\n  bool public paused;\n\n  //@notice Swap state\n  bool public swapPaused;\n\n  /*//////////////////////////////////////////////////////////////\n                                 ERROR\n    //////////////////////////////////////////////////////////////*/\n\n  error ValueOutOfBounds();\n  error ActionPaused();\n  error SlippageExceed();\n  error SwapDisabled(address asset);\n  error AssetNotAllowedInGovernancePool(address asset);\n  error DuplicatesAssets();\n\n  /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n  event Pause(uint256 timestamp);\n  event Unpause(uint256 timestamp);\n  event Deposit(uint32 requestId, RequestData request);\n  event Withdraw(uint32 requestId, RequestData request);\n  event Swap(uint32 requestId, RequestData request);\n\n  /*//////////////////////////////////////////////////////////////\n                            CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n  constructor(address _oracleModule, address _govModule, uint8 _deviationThreshold)\n    Ownable(msg.sender)\n  {\n    oracleModule = IOracle(_oracleModule);\n    GOVERNANCE_MODULE = IGovernanceModule(_govModule);\n    updateDeviationThreshold(_deviationThreshold);\n  }\n\n  /*//////////////////////////////////////////////////////////////\n                                GUARD\n    //////////////////////////////////////////////////////////////*/\n\n  ///@notice Pause the protocol\n  function pauseProtocol() public onlyGuard {\n    paused = true;\n    emit Pause(block.timestamp);\n  }\n\n  ///@notice Pause the swaps\n  function pauseSwap() external onlyGuard {\n    swapPaused = true;\n    emit Pause(block.timestamp);\n  }\n\n  /*//////////////////////////////////////////////////////////////\n                                 OWNER\n    //////////////////////////////////////////////////////////////*/\n\n  ///@notice sets the addres of fyde contract\n  ///@param _fyde address of fyde\n  function setFyde(address _fyde) external onlyOwner {\n    fyde = IFyde(_fyde);\n  }\n\n  ///@notice Set the oracle module\n  function setOracleModule(address _oracleModule) external onlyOwner {\n    oracleModule = IOracle(_oracleModule);\n  }\n\n  ///@notice Set the tax module\n  function setTaxModule(address _taxModule) external onlyOwner {\n    taxModule = ITaxModule(_taxModule);\n  }\n\n  ///@notice Change the deviation threshold\n  ///@dev 50 = 0.5 % of deviation\n  function updateDeviationThreshold(uint16 _threshold) public onlyOwner {\n    // We bound the threshold between 0.1 % to 10%\n    if (_threshold < 10 || _threshold > 1000) revert ValueOutOfBounds();\n    deviationThreshold = _threshold;\n  }\n\n  ///@notice Approve Fyde to transfer token from relayer, should be called once per asset\n  function approveFyde(address[] calldata _assets) external onlyOwner {\n    for (uint256 i; i < _assets.length; ++i) {\n      IERC20(_assets[i]).safeApprove(address(fyde), type(uint256).max);\n    }\n  }\n\n  ///@notice Collect and send token fees (from tax fees) to an external address\n  ///@param _asset Address to send fees to\n  ///@param _recipient Address to send fees to\n  ///@param _amount Amount to send\n  function collectFees(address _asset, address _recipient, uint256 _amount) external onlyOwner {\n    IERC20(_asset).safeTransfer(_recipient, _amount);\n  }\n\n  ///@notice Unpause the protocol\n  function unpauseProtocol() external onlyOwner {\n    paused = false;\n    emit Unpause(block.timestamp);\n  }\n\n  ///@notice Unpause the swaps\n  function unpauseSwap() external onlyOwner {\n    swapPaused = false;\n    emit Unpause(block.timestamp);\n  }\n\n  /*//////////////////////////////////////////////////////////////\n                            EXT USER ENTRY POINT\n    //////////////////////////////////////////////////////////////*/\n\n  ///@notice Entry function for depositing, can be a standard deposit or a governance\n  /// deposit\n  ///@param _userRequest struct containing data\n  ///@param _keepGovRights If true make a governance\n  ///@param _minTRSYExpected Slippage parameter ensuring minimum amout of TRSY to be received\n  function deposit(\n    UserRequest[] calldata _userRequest,\n    bool _keepGovRights,\n    uint256 _minTRSYExpected\n  ) external whenNotPaused onlyUser {\n    address[] memory assetIn = new address[](_userRequest.length);\n    uint256[] memory amountIn = new uint256[](_userRequest.length);\n\n    for (uint256 i; i < _userRequest.length; ++i) {\n      // Unpack data\n      assetIn[i] = _userRequest[i].asset;\n      amountIn[i] = _userRequest[i].amount;\n    }\n\n    _checkForDuplicates(assetIn);\n\n    if (_keepGovRights) _checkIsAllowedInGov(assetIn);\n\n    RequestData memory req = RequestData({\n      id: nonce,\n      requestor: address(this),\n      assetIn: assetIn,\n      amountIn: amountIn,\n      assetOut: new address[](0),\n      amountOut: new uint256[](0),\n      keepGovRights: _keepGovRights,\n      slippageChecker: _minTRSYExpected\n    });\n\n    nonce++;\n\n    uint256 currentAUM = fyde.getProtocolAUM();\n\n    // Cache prices in oracle (gas savings when fyde reads prices)\n    _enableOracleCache(assetIn);\n\n    // get params for tax calculation from tax module\n    (ProcessParam[] memory processParam, uint256 sharesToMint,,) =\n      taxModule.getProcessParamDeposit(req, currentAUM);\n\n    // Slippage checker\n    if (req.slippageChecker > sharesToMint) revert SlippageExceed();\n\n    // Transfer assets to Relayer\n    for (uint256 i; i < req.assetIn.length; ++i) {\n      IERC20(req.assetIn[i]).safeTransferFrom(msg.sender, address(this), req.amountIn[i]);\n    }\n\n    // Deposit\n    fyde.processDeposit(currentAUM, req);\n\n    if (_keepGovRights) {\n      for (uint256 i; i < processParam.length; ++i) {\n        // send staked trsy to the user\n        address sTrsy = GOVERNANCE_MODULE.assetToStrsy(req.assetIn[i]);\n        uint256 strsyBal = IERC20(sTrsy).balanceOf(address(this));\n        uint256 toTransfer =\n          strsyBal >= processParam[i].sharesAfterTax ? processParam[i].sharesAfterTax : strsyBal;\n        IERC20(sTrsy).transfer(msg.sender, toTransfer);\n\n        // unstake tax amount to get standard trsy\n        uint256 taxTrsy = strsyBal - toTransfer;\n        if (taxTrsy != 0) GOVERNANCE_MODULE.unstakeGov(taxTrsy, req.assetIn[i]);\n      }\n    } else {\n      // send trsy to user\n      IERC20(address(fyde)).transfer(msg.sender, sharesToMint);\n    }\n\n    _disableOracleCache();\n\n    emit Deposit(req.id, req);\n  }\n\n  ///@notice Entry function for withdrawing\n  ///@param _userRequest struct containing data\n  ///@param _maxTRSYToPay Slippage parameter ensure maximum amout of TRSY willing to pay\n  function withdraw(UserRequest[] calldata _userRequest, uint256 _maxTRSYToPay)\n    external\n    whenNotPaused\n    onlyUser\n  {\n    address[] memory assetOut = new address[](_userRequest.length);\n    uint256[] memory amountOut = new uint256[](_userRequest.length);\n\n    for (uint256 i; i < _userRequest.length; i++) {\n      assetOut[i] = _userRequest[i].asset;\n      amountOut[i] = _userRequest[i].amount;\n    }\n\n    _checkForDuplicates(assetOut);\n\n    RequestData memory req = RequestData({\n      id: nonce,\n      requestor: address(this),\n      assetIn: new address[](0),\n      amountIn: new uint256[](0),\n      assetOut: assetOut,\n      amountOut: amountOut,\n      keepGovRights: false,\n      slippageChecker: _maxTRSYToPay\n    });\n\n    nonce++;\n\n    uint256 currentAUM = fyde.getProtocolAUM();\n\n    _enableOracleCache(assetOut);\n\n    // get params for tax calculation from tax module\n    (, uint256 totalSharesToBurn,,,) = taxModule.getProcessParamWithdraw(req, currentAUM);\n\n    if (totalSharesToBurn > req.slippageChecker) revert SlippageExceed();\n\n    // Transfer TRSY to Relayer\n    IERC20(address(fyde)).transferFrom(msg.sender, address(this), totalSharesToBurn);\n\n    // Withdraw\n    fyde.processWithdraw(currentAUM, req);\n\n    // Transfer assets to user\n    for (uint256 i; i < req.assetOut.length; ++i) {\n      IERC20(req.assetOut[i]).safeTransfer(msg.sender, req.amountOut[i]);\n    }\n\n    _disableOracleCache();\n\n    emit Withdraw(req.id, req);\n  }\n\n  ///@notice Function used by user to make a (single-token) withdrawal from their governance proxy\n  ///@param _userRequest struct containing data\n  ///@param _user address of user who makes the withdraw\n  ///@param _maxTRSYToPay maximum amout of stTRSY willing to pay, otherwise withdraw reverts\n  ///@dev owner of fyde can force withdraw for other users\n  function governanceWithdraw(UserRequest memory _userRequest, address _user, uint256 _maxTRSYToPay)\n    external\n    whenNotPaused\n    onlyUser\n  {\n    if (msg.sender != _user && msg.sender != owner) revert Unauthorized();\n\n    address[] memory assetOut = new address[](1);\n    uint256[] memory amountOut = new uint256[](1);\n\n    assetOut[0] = _userRequest.asset;\n    amountOut[0] = _userRequest.amount;\n\n    // for withdraw, assetIn and amountIn are set to empty array\n    RequestData memory request = RequestData({\n      id: nonce,\n      requestor: _user,\n      assetIn: new address[](0),\n      amountIn: new uint256[](0),\n      assetOut: assetOut,\n      amountOut: amountOut,\n      keepGovRights: true,\n      slippageChecker: _maxTRSYToPay\n    });\n\n    nonce++;\n\n    uint256 currentAUM = fyde.getProtocolAUM();\n\n    fyde.processWithdraw(currentAUM, request);\n    emit Withdraw(request.id, request);\n  }\n\n  /*//////////////////////////////////////////////////////////////\n                               SWAP\n    //////////////////////////////////////////////////////////////*/\n\n  function swap(address _assetIn, uint256 _amountIn, address _assetOut, uint256 _minAmountOut)\n    external\n    whenSwapNotPaused\n    onlySwapper\n  {\n    address[] memory assetIn = new address[](1);\n    uint256[] memory amountIn = new uint256[](1);\n    address[] memory assetOut = new address[](1);\n    uint256[] memory amountOut = new uint256[](1);\n\n    assetIn[0] = _assetIn;\n    amountIn[0] = _amountIn;\n    assetOut[0] = _assetOut;\n\n    RequestData memory req = RequestData({\n      id: nonce,\n      requestor: address(this),\n      assetIn: assetIn,\n      amountIn: amountIn,\n      assetOut: assetOut,\n      amountOut: amountOut,\n      keepGovRights: false,\n      slippageChecker: _minAmountOut\n    });\n\n    nonce++;\n\n    uint256 currentAUM = fyde.getProtocolAUM();\n\n    address[] memory assetsSwap = new address[](2);\n    assetsSwap[0] = _assetIn;\n    assetsSwap[1] = _assetOut;\n\n    _enableOracleCache(assetsSwap);\n\n    // get params for tax calculation from taxModule\n    (uint256 amountOutTaxed,) =\n      taxModule.getSwapAmountOut(req.assetIn[0], req.amountIn[0], req.assetOut[0], currentAUM);\n\n    // Transfer asset to Relayer\n    IERC20(req.assetIn[0]).safeTransferFrom(msg.sender, address(this), req.amountIn[0]);\n\n    fyde.processSwap(currentAUM, req);\n\n    uint256 tokenBalance = IERC20(assetOut[0]).balanceOf(address(this));\n\n    amountOutTaxed = amountOutTaxed > tokenBalance ? tokenBalance : amountOutTaxed;\n    if (amountOutTaxed < req.slippageChecker) revert SlippageExceed();\n\n    // Transfer assets to swapper\n    IERC20(req.assetOut[0]).safeTransfer(msg.sender, amountOutTaxed);\n\n    emit Swap(req.id, req);\n\n    // deposit tax to receive trsy\n    amountIn[0] = tokenBalance - amountOutTaxed;\n    if (amountIn[0] > 0) {\n      req = RequestData({\n        id: nonce,\n        requestor: address(this),\n        assetIn: assetOut,\n        amountIn: amountIn,\n        assetOut: new address[](0),\n        amountOut: new uint256[](0),\n        keepGovRights: false,\n        slippageChecker: 0\n      });\n\n      nonce++;\n\n      currentAUM = fyde.getProtocolAUM();\n      fyde.processDeposit(currentAUM, req);\n    }\n\n    _disableOracleCache();\n  }\n\n  /*//////////////////////////////////////////////////////////////\n                            Keeper FUNCTIONS\n    //////////////////////////////////////////////////////////////*/\n\n  ///@notice Offchain checker for AUM deviation\n  function checkUpkeep(bytes calldata checkData)\n    external\n    view\n    returns (bool upkeepNeeded, bytes memory performData)\n  {\n    (uint256 updateFactor, uint256 pauseFactor, bool isChainlink) =\n      abi.decode(checkData, (uint256, uint256, bool));\n\n    uint256 aum = fyde.getProtocolAUM();\n    uint256 nAum = fyde.computeProtocolAUM();\n\n    // AUM in range of deviation threshold times update factor do nothing\n    if (PercentageMath._isInRange(aum, nAum, updateFactor * deviationThreshold / 100)) {\n      return (false, \"AUM is in range\");\n    }\n\n    // if stored AUM exceeds the maximum deviation threshold by the pause factor\n    // something is wrong and we stop the protocol\n    if (!PercentageMath._isInRange(aum, nAum, pauseFactor * deviationThreshold / 100) && !paused) {\n      if (isChainlink) return (true, abi.encode(false, 0));\n      return (true, abi.encodeCall(this.performUpkeep, (abi.encode(false, 0))));\n    }\n\n    // if not in range and not outside the wider range, update AUM\n    int256 diffAUM = int256(nAum) - int256(aum);\n    if (isChainlink) return (true, abi.encode(true, diffAUM));\n    return (true, abi.encodeCall(this.performUpkeep, (abi.encode(true, diffAUM))));\n  }\n\n  function performUpkeep(bytes calldata performData) external {\n    (bool updateAum, int256 diffAUM) = abi.decode(performData, (bool, int256));\n\n    if (!updateAum) {\n      pauseProtocol();\n    } else {\n      uint256 nAum = uint256(int256(fyde.getProtocolAUM()) + diffAUM);\n      updateProtocolAUM(nAum);\n    }\n  }\n\n  ///@notice Update the protocol AUM, called by Keeper\n  function updateProtocolAUM(uint256 nAum) public onlyKeeper {\n    fyde.updateProtocolAUM(nAum);\n  }\n  /*//////////////////////////////////////////////////////////////\n                                 INTERNAL\n    //////////////////////////////////////////////////////////////*/\n\n  function _enableOracleCache(address[] memory assets) internal {\n    AssetInfo[] memory assetInfos = new AssetInfo[](assets.length);\n    for (uint256 i; i < assets.length; ++i) {\n      assetInfos[i] = fyde.assetInfo(assets[i]);\n    }\n    oracleModule.useCache(assets, assetInfos);\n  }\n\n  function _disableOracleCache() internal {\n    oracleModule.disableCache();\n  }\n\n  function _checkIsAllowedInGov(address[] memory _assets) internal view {\n    address notAllowedInGovAsset = GOVERNANCE_MODULE.isAnyNotOnGovWhitelist(_assets);\n    if (notAllowedInGovAsset != address(0x0)) {\n      revert AssetNotAllowedInGovernancePool(notAllowedInGovAsset);\n    }\n  }\n\n  function _checkForDuplicates(address[] memory _assetList) internal pure {\n    for (uint256 idx; idx < _assetList.length - 1; idx++) {\n      for (uint256 idx2 = idx + 1; idx2 < _assetList.length; idx2++) {\n        if (_assetList[idx] == _assetList[idx2]) revert DuplicatesAssets();\n      }\n    }\n  }\n\n  function _uint2str(uint256 _i) internal pure returns (string memory) {\n    if (_i == 0) return \"0\";\n    uint256 j = _i;\n    uint256 len;\n    while (j != 0) {\n      len++;\n      j /= 10;\n    }\n    bytes memory bstr = new bytes(len);\n    uint256 k = len;\n    while (_i != 0) {\n      k = k - 1;\n      uint8 temp = (48 + uint8(_i - (_i / 10) * 10));\n      bytes1 b1 = bytes1(temp);\n      bstr[k] = b1;\n      _i /= 10;\n    }\n    return string(bstr);\n  }\n\n  modifier whenNotPaused() {\n    if (paused) revert ActionPaused();\n    _;\n  }\n\n  modifier whenSwapNotPaused() {\n    if (swapPaused) revert ActionPaused();\n    _;\n  }\n}\n"
      },
      "src/core/QuarantineList.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity 0.8.19;\n\nimport {AccessControl} from \"./AccessControl.sol\";\n\n///@title QuarantineList contract\n///@notice Handle the logic for the quarantine list\nabstract contract QuarantineList is AccessControl {\n  /// -----------------------------\n  ///         Storage\n  /// -----------------------------\n  uint256 public min_quarantine_duration = 1 days;\n  mapping(address => uint128) public quarantineList;\n\n  /// -----------------------------\n  ///         Events\n  /// -----------------------------\n\n  event AddedToQuarantine(address asset, uint128 expirationTime);\n  event RemovedFromQuarantine(address asset);\n  /// -----------------------------\n  ///         Errors\n  /// -----------------------------\n\n  error AssetIsQuarantined(address asset);\n  error AssetIsNotQuarantined(address asset);\n  error ShortenedExpiration(uint128 currentExpiration, uint128 expiration);\n  error ShortQurantineDuration(uint128 duration);\n\n  /// -----------------------------\n  ///         Admin external\n  /// -----------------------------\n\n  ///@notice Set the minimum quarantine duration\n  ///@param _min_quarantine_duration the new minimum quarantine duration\n  function set_min_quarantine_duration(uint256 _min_quarantine_duration) external onlyOwner {\n    min_quarantine_duration = _min_quarantine_duration;\n  }\n\n  ///@notice Add an asset to the quarantine list\n  ///@param _asset the address of the assset to be added to the quarantine list\n  ///@param _duration the time (in seconds) that the asset should stay in quarantine\n  function addToQuarantine(address _asset, uint128 _duration) external onlyGuard {\n    if (_duration < min_quarantine_duration) revert ShortQurantineDuration(_duration);\n\n    // gas savings\n    uint128 expiration = uint128(block.timestamp) + _duration;\n    uint128 currentExpiration = quarantineList[_asset];\n\n    // the new expiration cannot be before the curent one i.e. expiration cannot be reduced, just\n    // extended\n    if (expiration <= currentExpiration) revert ShortenedExpiration(currentExpiration, expiration);\n\n    quarantineList[_asset] = expiration;\n\n    emit AddedToQuarantine(_asset, expiration);\n  }\n\n  ///@notice Remove an asset from the quarantine list\n  ///@param _asset the address of the assset to be removed from the quarantine list\n  function removeFromQuarantine(address _asset) external onlyGuard {\n    // If the asset has nto been quarantined or the duarion period has expired then revert\n    if (quarantineList[_asset] < uint128(block.timestamp)) revert AssetIsNotQuarantined(_asset);\n\n    // just set the duration to zero to remove from quarantine\n    quarantineList[_asset] = 0;\n    emit RemovedFromQuarantine(_asset);\n  }\n\n  /// -----------------------------\n  ///    External view functions\n  /// -----------------------------\n\n  ///@notice Check if an asset is quarantined\n  ///@param _asset the address of the assset to be checked\n  function isQuarantined(address _asset) public view returns (bool) {\n    return quarantineList[_asset] >= uint128(block.timestamp);\n  }\n\n  ///@notice Check if any asset from a given list is quarantined\n  ///@param _assets an array of asset addresses that need to be checked\n  ///@return address of first quarantined asset or address(0x0) if none quarantined\n  function isAnyQuarantined(address[] memory _assets) public view returns (address) {\n    for (uint256 i = 0; i < _assets.length;) {\n      if (isQuarantined(_assets[i])) return _assets[i];\n\n      unchecked {\n        ++i;\n      }\n    }\n    return address(0x0);\n  }\n}\n"
      },
      "src/core/Structs.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nstruct AssetInfo {\n  uint72 targetConcentration;\n  address uniswapPool;\n  int72 incentiveFactor;\n  uint8 assetDecimals;\n  uint8 quoteTokenDecimals;\n  address uniswapQuoteToken;\n  bool isSupported;\n}\n\nstruct ProtocolData {\n  ///@notice Protocol AUM in USD\n  uint256 aum;\n  ///@notice multiplicator for the tax equation, 100% = 100e18\n  uint72 taxFactor;\n  ///@notice Max deviation allowed between AUM from keeper and registry\n  uint16 maxAumDeviationAllowed; // Default val 200 == 2 %\n  ///@notice block number where AUM was last updated\n  uint48 lastAUMUpdateBlock;\n  ///@notice annual fee on AUM, in % per year 100% = 100e18\n  uint72 managementFee;\n  ///@notice last block.timestamp when fee was collected\n  uint48 lastFeeCollectionTime;\n}\n\nstruct UserRequest {\n  address asset;\n  uint256 amount;\n}\n\nstruct RequestData {\n  uint32 id;\n  address requestor;\n  address[] assetIn;\n  uint256[] amountIn;\n  address[] assetOut;\n  uint256[] amountOut;\n  bool keepGovRights;\n  uint256 slippageChecker;\n}\n\nstruct RequestQ {\n  uint64 start;\n  uint64 end;\n  mapping(uint64 => RequestData) requestData;\n}\n\nstruct ProcessParam {\n  uint256 targetConc;\n  uint256 currentConc;\n  uint256 usdValue;\n  uint256 taxableAmount;\n  uint256 taxInUSD;\n  uint256 sharesBeforeTax;\n  uint256 sharesAfterTax;\n}\n\nstruct RebalanceParam {\n  address asset;\n  uint256 assetTotalAmount;\n  uint256 assetProxyAmount;\n  uint256 assetPrice;\n  uint256 sTrsyTotalSupply;\n  uint256 trsyPrice;\n}\n"
      },
      "src/utils/Ownable.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.19;\n\n///@title Ownable contract\n/// @notice Simple 2step owner authorization combining solmate and OZ implementation\nabstract contract Ownable {\n  /*//////////////////////////////////////////////////////////////\n                             STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n  ///@notice Address of the owner\n  address public owner;\n\n  ///@notice Address of the pending owner\n  address public pendingOwner;\n\n  /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n  event OwnershipTransferred(address indexed user, address indexed newOner);\n  event OwnershipTransferStarted(address indexed user, address indexed newOwner);\n  event OwnershipTransferCanceled(address indexed pendingOwner);\n\n  /*//////////////////////////////////////////////////////////////\n                                 ERROR\n    //////////////////////////////////////////////////////////////*/\n\n  error Unauthorized();\n\n  /*//////////////////////////////////////////////////////////////\n                               CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n  constructor(address _owner) {\n    owner = _owner;\n\n    emit OwnershipTransferred(address(0), _owner);\n  }\n\n  /*//////////////////////////////////////////////////////////////\n                             OWNERSHIP LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n  ///@notice Transfer ownership to a new address\n  ///@param newOwner address of the new owner\n  ///@dev newOwner have to acceptOwnership\n  function transferOwnership(address newOwner) external onlyOwner {\n    pendingOwner = newOwner;\n    emit OwnershipTransferStarted(msg.sender, pendingOwner);\n  }\n\n  ///@notice NewOwner accept the ownership, it transfer the ownership to newOwner\n  function acceptOwnership() external {\n    if (msg.sender != pendingOwner) revert Unauthorized();\n    address oldOwner = owner;\n    owner = pendingOwner;\n    delete pendingOwner;\n    emit OwnershipTransferred(oldOwner, owner);\n  }\n\n  ///@notice Cancel the ownership transfer\n  function cancelTransferOwnership() external onlyOwner {\n    emit OwnershipTransferCanceled(pendingOwner);\n    delete pendingOwner;\n  }\n\n  modifier onlyOwner() {\n    if (msg.sender != owner) revert Unauthorized();\n    _;\n  }\n}\n"
      },
      "src/utils/PercentageMath.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nlibrary PercentageMath {\n  ///\tCONSTANTS ///\n\n  uint256 internal constant PERCENTAGE_FACTOR = 1e4; // 100.00%\n  uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4; // 50.00%\n  uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;\n  uint256 internal constant MAX_UINT256_MINUS_HALF_PERCENTAGE = 2 ** 256 - 1 - 0.5e4;\n\n  /// INTERNAL ///\n\n  ///@notice Check if value are within the range\n  function _isInRange(uint256 valA, uint256 valB, uint256 deviationThreshold)\n    internal\n    pure\n    returns (bool)\n  {\n    uint256 lowerBound = percentSub(valA, deviationThreshold);\n    uint256 upperBound = percentAdd(valA, deviationThreshold);\n    if (valB < lowerBound || valB > upperBound) return false;\n    else return true;\n  }\n\n  /// @notice Executes a percentage addition (x * (1 + p)), rounded up.\n  /// @param x The value to which to add the percentage.\n  /// @param percentage The percentage of the value to add.\n  /// @return y The result of the addition.\n  function percentAdd(uint256 x, uint256 percentage) internal pure returns (uint256 y) {\n    // Must revert if\n    // PERCENTAGE_FACTOR + percentage > type(uint256).max\n    //     or x * (PERCENTAGE_FACTOR + percentage) + HALF_PERCENTAGE_FACTOR > type(uint256).max\n    // <=> percentage > type(uint256).max - PERCENTAGE_FACTOR\n    //     or x > (type(uint256).max - HALF_PERCENTAGE_FACTOR) / (PERCENTAGE_FACTOR + percentage)\n    // Note: PERCENTAGE_FACTOR + percentage >= PERCENTAGE_FACTOR > 0\n    assembly {\n      y := add(PERCENTAGE_FACTOR, percentage) // Temporary assignment to save gas.\n\n      if or(\n        gt(percentage, sub(MAX_UINT256, PERCENTAGE_FACTOR)),\n        gt(x, div(MAX_UINT256_MINUS_HALF_PERCENTAGE, y))\n      ) { revert(0, 0) }\n\n      y := div(add(mul(x, y), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)\n    }\n  }\n\n  /// @notice Executes a percentage subtraction (x * (1 - p)), rounded up.\n  /// @param x The value to which to subtract the percentage.\n  /// @param percentage The percentage of the value to subtract.\n  /// @return y The result of the subtraction.\n  function percentSub(uint256 x, uint256 percentage) internal pure returns (uint256 y) {\n    // Must revert if\n    // percentage > PERCENTAGE_FACTOR\n    //     or x * (PERCENTAGE_FACTOR - percentage) + HALF_PERCENTAGE_FACTOR > type(uint256).max\n    // <=> percentage > PERCENTAGE_FACTOR\n    //     or ((PERCENTAGE_FACTOR - percentage) > 0 and x > (type(uint256).max -\n    // HALF_PERCENTAGE_FACTOR) / (PERCENTAGE_FACTOR - percentage))\n    assembly {\n      y := sub(PERCENTAGE_FACTOR, percentage) // Temporary assignment to save gas.\n\n      if or(\n        gt(percentage, PERCENTAGE_FACTOR), mul(y, gt(x, div(MAX_UINT256_MINUS_HALF_PERCENTAGE, y)))\n      ) { revert(0, 0) }\n\n      y := div(add(mul(x, y), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)\n    }\n  }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(IERC20 token, address spender, uint256 value) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n        require(\n            (value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\n     * Revert on invalid signature.\n     */\n    function safePermit(\n        IERC20Permit token,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal {\n        uint256 nonceBefore = token.nonces(owner);\n        token.permit(owner, spender, value, deadline, v, r, s);\n        uint256 nonceAfter = token.nonces(owner);\n        require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        require(returndata.length == 0 || abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n        // and not revert is the subcall reverts.\n\n        (bool success, bytes memory returndata) = address(token).call(data);\n        return\n            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n"
      },
      "src/interfaces/IFyde.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.19;\n\nimport {RequestData, RebalanceParam, ProcessParam, AssetInfo} from \"src/core/Structs.sol\";\n\ninterface IFyde {\n  function protocolData() external view returns (uint256, uint72, uint16, uint48, uint72, uint48);\n\n  function assetInfo(address) external view returns (AssetInfo memory);\n\n  function isAnyNotSupported(address[] calldata _assets) external view returns (address);\n\n  function isSwapAllowed(address[] calldata _assets) external view returns (address);\n\n  function computeProtocolAUM() external view returns (uint256);\n\n  function getProtocolAUM() external view returns (uint256);\n\n  function updateProtocolAUM(uint256) external;\n\n  function processDeposit(uint256, RequestData calldata) external returns (uint256);\n\n  function processWithdraw(uint256, RequestData calldata) external returns (uint256);\n\n  function totalSupply() external view returns (uint256);\n\n  function setOracleModule(address _oracle) external;\n\n  function oracleModule() external view returns (address);\n\n  function setRelayer(address _relayer) external;\n\n  function RELAYER() external view returns (address);\n\n  function totalAssetAccounting(address) external view returns (uint256);\n\n  function proxyAssetAccounting(address) external view returns (uint256);\n\n  function standardAssetAccounting(address) external view returns (uint256);\n\n  function getQuote(address, uint256) external view returns (uint256);\n\n  function getAssetDecimals(address) external view returns (uint8);\n\n  function collectManagementFee() external;\n\n  function getAssetsListLength() external view returns (uint256);\n\n  function assetsList(uint256 index) external view returns (address);\n\n  function processSwap(uint256, RequestData calldata) external returns (int256);\n\n  function owner() external view returns (address);\n\n  function getProcessParamDeposit(RequestData memory _req, uint256 _protocolAUM)\n    external\n    view\n    returns (\n      ProcessParam[] memory processParam,\n      uint256 sharesToMint,\n      uint256 taxInTRSY,\n      uint256 totalUsdDeposit\n    );\n\n  function getProcessParamWithdraw(RequestData calldata _req, uint256 _protocolAUM)\n    external\n    view\n    returns (\n      ProcessParam[] memory processParam,\n      uint256 totalSharesToBurn,\n      uint256 sharesToBurnBeforeTax,\n      uint256 taxInTRSY,\n      uint256 totalUsdWithdraw\n    );\n\n  function acceptOwnership() external;\n\n  // GOVERNANCE ACCESS FUNCTIONS\n\n  function transferAsset(address _asset, address _recipient, uint256 _amount) external;\n\n  function getRebalanceParams(address _asset) external view returns (RebalanceParam memory);\n\n  function updateAssetProxyAmount(address _asset, uint256 _amount) external;\n}\n"
      },
      "src/interfaces/IGovernanceModule.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.19;\n\ninterface IGovernanceModule {\n  function fyde() external view returns (address);\n\n  function proxyImplementation() external view returns (address);\n\n  function proxyBalance(address proxy, address asset) external view returns (uint256);\n\n  function strsyBalance(address _user, address _govToken) external view returns (uint256 balance);\n\n  function assetToStrsy(address _asset) external view returns (address);\n\n  function userToProxy(address _user) external view returns (address);\n\n  function proxyToUser(address _proxy) external view returns (address);\n\n  function isOnGovernanceWhitelist(address _asset) external view returns (bool);\n\n  function getAllGovUsers() external view returns (address[] memory);\n\n  function isAnyNotOnGovWhitelist(address[] calldata _assets) external view returns (address);\n\n  function getUserGTAllowance(uint256 _TRSYAmount, address _token) external view returns (uint256);\n\n  function govDeposit(\n    address _depositor,\n    address[] calldata _govToken,\n    uint256[] calldata _amount,\n    uint256[] calldata _amountTRSY,\n    uint256 _totalTRSY\n  ) external returns (address proxy);\n\n  function govWithdraw(\n    address _user,\n    address _asset,\n    uint256 _amountToWithdraw,\n    uint256 _trsyToBurn\n  ) external;\n\n  function onStrsyTransfer(address sender, address _recipient) external;\n\n  function unstakeGov(uint256 _amount, address _asset) external;\n\n  function rebalanceProxy(address _proxy, address _asset, address[] memory _usersToRebalance)\n    external;\n\n  function acceptOwnership() external;\n}\n"
      },
      "src/interfaces/IOracle.sol": {
        "content": "//SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.19;\n\nimport {AssetInfo} from \"../core/Structs.sol\";\n\ninterface IOracle {\n  function getPriceInUSD(address, AssetInfo calldata) external view returns (uint256);\n\n  function getGweiPrice() external view returns (uint256);\n\n  function useCache(address[] calldata addr, AssetInfo[] calldata assetInfo) external;\n\n  function disableCache() external;\n}\n"
      },
      "src/interfaces/ITaxModule.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.19;\n\nimport {RequestData, ProcessParam} from \"src/core/Structs.sol\";\n\ninterface ITaxModule {\n  function getProcessParamDeposit(RequestData memory _req, uint256 _protocolAUM)\n    external\n    view\n    returns (\n      ProcessParam[] memory processParam,\n      uint256 sharesToMint,\n      uint256 taxInTRSY,\n      uint256 totalUsdDeposit\n    );\n\n  function getProcessParamWithdraw(RequestData calldata _req, uint256 _protocolAUM)\n    external\n    view\n    returns (\n      ProcessParam[] memory processParam,\n      uint256 totalSharesToBurn,\n      uint256 sharesToBurnBeforeTax,\n      uint256 taxInTRSY,\n      uint256 totalUsdWithdraw\n    );\n\n  function getSwapAmountOut(\n    address _assetIn,\n    uint256 _amountIn,\n    address _assetOut,\n    uint256 _protocolAUM\n  ) external view returns (uint256, int256);\n}\n"
      },
      "src/core/AccessControl.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\nimport {Ownable} from \"src/utils/Ownable.sol\";\n\n///@title AccessControl\n/// @notice Handles the different access authorizations for the relayer\nabstract contract AccessControl is Ownable {\n  /*//////////////////////////////////////////////////////////////\n                             STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n  ///@notice exclusive user - when != address(0x0), other users are removed from whitelist\n  ///        intended for short term use during incidence response, escrow, migration\n  address public exclusiveUser;\n\n  ///@notice guard authorization\n  mapping(address => bool) public isGuard;\n\n  ///@notice keeper authorization\n  mapping(address => bool) public isKeeper;\n\n  ///@notice user authorization, can use deposit, withdraw\n  mapping(address => bool) public isUser;\n\n  ///@notice swapper authorization, can use swap\n  mapping(address => bool) public isSwapper;\n\n  ///@notice incentive manager authorization, can set incentives for swaps on Fyde\n  mapping(address => bool) public isIncentiveManager;\n\n  /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n  event ExclusiveUserSet(address indexed);\n  event GuardAdded(address indexed);\n  event GuardRemoved(address indexed);\n  event KeeperAdded(address indexed);\n  event KeeperRemoved(address indexed);\n  event UserAdded(address indexed);\n  event UserRemoved(address indexed);\n  event IncentiveManagerAdded(address indexed);\n  event IncentiveManagerRemoved(address indexed);\n  event SwapperAdded(address indexed);\n  event SwapperRemoved(address indexed);\n\n  /*//////////////////////////////////////////////////////////////\n                               SETTER\n    //////////////////////////////////////////////////////////////*/\n\n  function setExclusiveUser(address _exclusiveUser) external onlyOwner {\n    exclusiveUser = _exclusiveUser;\n    emit ExclusiveUserSet(_exclusiveUser);\n  }\n\n  function addGuard(address _guard) external onlyOwner {\n    isGuard[_guard] = true;\n    emit GuardAdded(_guard);\n  }\n\n  function removeGuard(address _guard) external onlyOwner {\n    isGuard[_guard] = false;\n    emit GuardRemoved(_guard);\n  }\n\n  function addKeeper(address _keeper) external onlyOwner {\n    isKeeper[_keeper] = true;\n    emit KeeperAdded(_keeper);\n  }\n\n  function removeKeeper(address _keeper) external onlyOwner {\n    isKeeper[_keeper] = false;\n    emit KeeperRemoved(_keeper);\n  }\n\n  function addUser(address[] calldata _user) external onlyOwner {\n    for (uint256 i; i < _user.length; ++i) {\n      isUser[_user[i]] = true;\n      emit UserAdded(_user[i]);\n    }\n  }\n\n  function removeUser(address[] calldata _user) external onlyOwner {\n    for (uint256 i; i < _user.length; ++i) {\n      isUser[_user[i]] = false;\n      emit UserRemoved(_user[i]);\n    }\n  }\n\n  function addIncentiveManager(address _incentiveManager) external onlyOwner {\n    isIncentiveManager[_incentiveManager] = true;\n    emit IncentiveManagerAdded(_incentiveManager);\n  }\n\n  function removeIncentiveManager(address _incentiveManager) external onlyOwner {\n    isIncentiveManager[_incentiveManager] = false;\n    emit IncentiveManagerRemoved(_incentiveManager);\n  }\n\n  function addSwapper(address _swapper) external onlyOwner {\n    isSwapper[_swapper] = true;\n    emit SwapperAdded(_swapper);\n  }\n\n  function removeSwapper(address _swapper) external onlyOwner {\n    isSwapper[_swapper] = false;\n    emit SwapperRemoved(_swapper);\n  }\n\n  /*//////////////////////////////////////////////////////////////\n                               MODIFIER\n    //////////////////////////////////////////////////////////////*/\n\n  ///@notice only a registered keeper can access\n  modifier onlyKeeper() {\n    if (!isKeeper[msg.sender]) revert Unauthorized();\n    _;\n  }\n\n  ///@notice only a registered guard can access\n  modifier onlyGuard() {\n    if (!isGuard[msg.sender]) revert Unauthorized();\n    _;\n  }\n\n  ///@dev whitelisting address(0x0) disables whitelist -> full permissionless access\n  ///@dev setting exclusiveUser to != address(0x0) blocks everyone else\n  ///     - intended for escrow, incidence response and migration\n  modifier onlyUser() {\n    // if whitelist is not disabeld and user not whitelisted -> no access\n    if (!isUser[address(0x0)] && !isUser[msg.sender]) revert Unauthorized();\n    // if exclusive user exists and is not user -> no accesss\n    if (exclusiveUser != address(0x0) && exclusiveUser != msg.sender) revert Unauthorized();\n    _;\n  }\n\n  ///@dev whitelisting address(0x0) disables whitelist -> full permissionless access\n  ///@dev setting exclusiveUser to != address(0x0) blocks everyone else\n  ///     - intended for escrow, incidence response and migration\n  modifier onlySwapper() {\n    if (!isSwapper[address(0x0)] && !isSwapper[msg.sender]) revert Unauthorized();\n    if (exclusiveUser != address(0x0) && exclusiveUser != msg.sender) revert Unauthorized();\n    _;\n  }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     *\n     * Furthermore, `isContract` will also return true if the target contract within\n     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n     * which only has an effect at the end of a transaction.\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "@uniswap/v3-core/=lib/v3-core/",
        "@uniswap/v3-periphery/=lib/v3-periphery/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
        "forge-std/=lib/forge-std/src/",
        "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
        "openzeppelin/=lib/openzeppelin-contracts/contracts/",
        "solmate/=lib/solmate/src/",
        "synthetix-v3/=lib/synthetix-v3/",
        "v3-core/=lib/v3-core/contracts/",
        "v3-periphery/=lib/v3-periphery/contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "paris",
      "libraries": {}
    }
  }}