ETH Price: $3,690.21 (+3.39%)

Contract

0x94194DE310b99d3c8a5b8C0768cFCE7Aef81D9BE
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Update Protocol ...195863062024-04-05 1:36:11239 days ago1712280971IN
0x94194DE3...Aef81D9BE
0 ETH0.0007842718.18957137
Update Protocol ...195836502024-04-04 16:40:59239 days ago1712248859IN
0x94194DE3...Aef81D9BE
0 ETH0.0023456554.39587941
Process Requests195820972024-04-04 11:26:47239 days ago1712230007IN
0x94194DE3...Aef81D9BE
0 ETH0.0052260623.350779
Request Withdraw195820962024-04-04 11:26:35239 days ago1712229995IN
0x94194DE3...Aef81D9BE
0.00824001 ETH0.0047280620.66732593
Process Requests195816652024-04-04 9:59:35239 days ago1712224775IN
0x94194DE3...Aef81D9BE
0 ETH0.0048782922.44567521
Request Withdraw195816642024-04-04 9:59:23239 days ago1712224763IN
0x94194DE3...Aef81D9BE
0.00824001 ETH0.0039559317.29582453
Process Requests195814032024-04-04 9:06:59240 days ago1712221619IN
0x94194DE3...Aef81D9BE
0 ETH0.0049622823.67717327
Request Deposit195814022024-04-04 9:06:47240 days ago1712221607IN
0x94194DE3...Aef81D9BE
0.00803232 ETH0.0047404120.69744992
Process Requests195812842024-04-04 8:43:11240 days ago1712220191IN
0x94194DE3...Aef81D9BE
0 ETH0.0051277822.91164649
Request Withdraw195812832024-04-04 8:42:59240 days ago1712220179IN
0x94194DE3...Aef81D9BE
0.00803232 ETH0.0041497318.14026874
Process Requests195785472024-04-03 23:32:47240 days ago1712187167IN
0x94194DE3...Aef81D9BE
0 ETH0.0060035226.85986318
Request Withdraw195785462024-04-03 23:32:35240 days ago1712187155IN
0x94194DE3...Aef81D9BE
0.01441905 ETH0.0052077322.76404528
Update Protocol ...195727052024-04-03 3:56:11241 days ago1712116571IN
0x94194DE3...Aef81D9BE
0 ETH0.0011390326.41739162
Update Protocol ...195717282024-04-03 0:39:35241 days ago1712104775IN
0x94194DE3...Aef81D9BE
0 ETH0.00155436.04158967
Process Requests195702652024-04-02 19:44:35241 days ago1712087075IN
0x94194DE3...Aef81D9BE
0 ETH0.0116520255.67484655
Request Deposit195702642024-04-02 19:44:23241 days ago1712087063IN
0x94194DE3...Aef81D9BE
0.02786143 ETH0.0124040954.15833295
Update Protocol ...195678002024-04-02 11:24:11241 days ago1712057051IN
0x94194DE3...Aef81D9BE
0 ETH0.0013920332.28513295
Update Protocol ...195652012024-04-02 2:40:23242 days ago1712025623IN
0x94194DE3...Aef81D9BE
0 ETH0.0016256537.70345102
Update Protocol ...195620332024-04-01 16:00:47242 days ago1711987247IN
0x94194DE3...Aef81D9BE
0 ETH0.0022858853.01596152
Update Protocol ...195598162024-04-01 8:32:35243 days ago1711960355IN
0x94194DE3...Aef81D9BE
0 ETH0.0008932220.71639253
Process Requests195581032024-04-01 2:44:23243 days ago1711939463IN
0x94194DE3...Aef81D9BE
0 ETH0.0043807919.59973956
Request Withdraw195581022024-04-01 2:44:11243 days ago1711939451IN
0x94194DE3...Aef81D9BE
0.00894394 ETH0.0038459716.811527
Process Requests195529502024-03-31 9:19:47244 days ago1711876787IN
0x94194DE3...Aef81D9BE
0 ETH0.0049179422.00296903
Request Withdraw195529492024-03-31 9:19:35244 days ago1711876775IN
0x94194DE3...Aef81D9BE
0.00815549 ETH0.0043268618.9135861
Process Requests195528392024-03-31 8:57:35244 days ago1711875455IN
0x94194DE3...Aef81D9BE
0 ETH0.0047536522.71356827
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
195880392024-04-05 7:24:59239 days ago1712301899
0x94194DE3...Aef81D9BE
1.04614019 ETH
193751142024-03-06 9:04:11269 days ago1709715851
0x94194DE3...Aef81D9BE
0.86784168 ETH
193189342024-02-27 12:37:23276 days ago1709037443
0x94194DE3...Aef81D9BE
0.09906877 ETH
193188972024-02-27 12:29:59276 days ago1709036999
0x94194DE3...Aef81D9BE
0.09906877 ETH
193188782024-02-27 12:26:11276 days ago1709036771
0x94194DE3...Aef81D9BE
0.09906877 ETH
192685122024-02-20 11:06:47283 days ago1708427207
0x94194DE3...Aef81D9BE
0.79792783 ETH
191842992024-02-08 15:20:59295 days ago1707405659
0x94194DE3...Aef81D9BE
0.63053251 ETH
191202342024-01-30 15:30:59304 days ago1706628659
0x94194DE3...Aef81D9BE
1.21780993 ETH
189842372024-01-11 14:19:47323 days ago1704982787
0x94194DE3...Aef81D9BE
0.34132299 ETH
189469422024-01-06 8:18:23329 days ago1704529103
0x94194DE3...Aef81D9BE
0.02975896 ETH
189469422024-01-06 8:18:23329 days ago1704529103
0x94194DE3...Aef81D9BE
0.02975896 ETH
189469272024-01-06 8:15:23329 days ago1704528923
0x94194DE3...Aef81D9BE
0.02975896 ETH
189469132024-01-06 8:12:35329 days ago1704528755
0x94194DE3...Aef81D9BE
0.02975896 ETH
189469132024-01-06 8:12:35329 days ago1704528755
0x94194DE3...Aef81D9BE
0.02975896 ETH
189468922024-01-06 8:08:23329 days ago1704528503
0x94194DE3...Aef81D9BE
0.02975896 ETH
189468922024-01-06 8:08:23329 days ago1704528503
0x94194DE3...Aef81D9BE
0.02975896 ETH
189468732024-01-06 8:04:35329 days ago1704528275
0x94194DE3...Aef81D9BE
0.03033674 ETH
189468732024-01-06 8:04:35329 days ago1704528275
0x94194DE3...Aef81D9BE
0.03033674 ETH
189468442024-01-06 7:58:47329 days ago1704527927
0x94194DE3...Aef81D9BE
0.03033674 ETH
187350502023-12-07 14:36:59358 days ago1701959819
0x94194DE3...Aef81D9BE
0.36342664 ETH
187329492023-12-07 7:32:35359 days ago1701934355
0x94194DE3...Aef81D9BE
0.06864685 ETH
187329492023-12-07 7:32:35359 days ago1701934355
0x94194DE3...Aef81D9BE
0.06864685 ETH
187328812023-12-07 7:18:35359 days ago1701933515
0x94194DE3...Aef81D9BE
0.07204431 ETH
187328762023-12-07 7:17:35359 days ago1701933455
0x94194DE3...Aef81D9BE
0.07204431 ETH
187328612023-12-07 7:14:35359 days ago1701933275
0x94194DE3...Aef81D9BE
0.07204431 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Relayer

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 41 : Relayer.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

// Core
import {Fyde} from "./Fyde.sol";
import {QuarantineList} from "./core/QuarantineList.sol";
import {RequestQueue} from "./core/RequestQueue.sol";

// Structs
import {UserRequest, RequestData} from "./core/Structs.sol";

// Utils
import {BaseChecker} from "./utils/BaseChecker.sol";
import {Ownable} from "./utils/Ownable.sol";
import {PercentageMath} from "./utils/PercentageMath.sol";

// Interfaces
import {IFyde} from "./interfaces/IFyde.sol";
import {IGovernanceModule} from "./interfaces/IGovernanceModule.sol";
import {IOracle} from "./interfaces/IOracle.sol";

///@title Relayer
///@notice The relayer is the entry point contract for users to interact with the protocol.
///        User call request functions to make a deposit or a withdrawal, that request will be
/// queued and be processed by a keeper.
///        The relayer is monitored by a Gelato bot that will process the requests.
///        The relayer is also monitored by a Gelato bot that will update the protocol AUM.

contract Relayer is RequestQueue, QuarantineList, BaseChecker {
  /*//////////////////////////////////////////////////////////////
                                 STORAGE
    //////////////////////////////////////////////////////////////*/

  ///@notice Fyde contract
  IFyde public fyde;

  ///@notice OracleModule contract
  IOracle public oracleModule;

  //@notice GovernanceModule contract
  IGovernanceModule public immutable GOVERNANCE_MODULE;

  //@notice Max number assets accepted in one request
  uint8 public constant MAX_ASSET_TO_REQUEST = 5;

  ///@notice Max requests to be processed in one batch
  uint8 public constant MAX_BATCH_SIZE = 3;

  ///@dev Only used for tracking events offchain
  uint32 public nonce;

  ///@notice Threshold of deviation for updating AUM
  uint16 public deviationThreshold;

  ///@notice State of the protocol
  bool public paused;

  //@notice Swap state
  bool public swapPaused;

  ///@notice Map the relayer action to the gas usage
  ///@dev We hash a string for simplicity : keccak256("Deposit_Standard_2") => Deposit 2 assets in
  /// standard pool
  mapping(bytes32 => uint256) public actionToGasUsage;

  /*//////////////////////////////////////////////////////////////
                                 ERROR
    //////////////////////////////////////////////////////////////*/

  error NoRequest();
  error ValueOutOfBounds();
  error ActionPaused();
  error DuplicatesAssets();
  error IncorrectNumOfAsset();
  error SlippageExceed(uint256 amountOut, uint256 minAmountOut);
  error NotEnoughGas(uint256 received, uint256 required);
  error AssetNotSupported(address asset);
  error SwapDisabled(address asset);
  error AssetPriceNotAvailable();
  error AssetNotAllowedInGovernancePool(address asset);

  /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

  event Pause(uint256 timestamp);
  event Unpause(uint256 timestamp);
  event DepositRequested(uint32 requestId, RequestData request);
  event WithdrawRequested(uint32 requestId, RequestData request);
  event ProcessRequestSuccess(uint32 requestId);
  event ProcessRequestFailed(uint32 requestId, bytes data);
  event SwapRequested(uint32 requestId, RequestData request);

  /*//////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

  ///@dev _relayerActions are a hash of a string, like keccak256("Deposit_Standard_1") -> Deposit 1
  /// asset in standard pool
  constructor(
    address _oracleModule,
    address _govModule,
    uint8 _deviationThreshold,
    bytes32[] memory _relayerActions,
    uint256[] memory _gasUsages
  ) Ownable(msg.sender) {
    oracleModule = IOracle(_oracleModule);
    GOVERNANCE_MODULE = IGovernanceModule(_govModule);
    updateDeviationThreshold(_deviationThreshold);

    for (uint256 i; i < _relayerActions.length; i++) {
      actionToGasUsage[_relayerActions[i]] = _gasUsages[i];
    }
  }

  /*//////////////////////////////////////////////////////////////
                                GUARD
    //////////////////////////////////////////////////////////////*/

  ///@notice Pause the protocol
  function pauseProtocol() external onlyGuard {
    paused = true;
    emit Pause(block.timestamp);
  }

  ///@notice Unpause the protocol
  function unpauseProtocol() external onlyGuard {
    paused = false;
    emit Unpause(block.timestamp);
  }

  ///@notice Pause the swaps
  function pauseSwap() external onlyGuard {
    swapPaused = true;
    emit Pause(block.timestamp);
  }

  ///@notice Unpause the swaps
  function unpauseSwap() external onlyGuard {
    swapPaused = false;
    emit Unpause(block.timestamp);
  }

  ///@notice Delete a request from the queue
  function deleteRequest(uint64 _numRequests) external onlyGuard {
    _deleteRequests(_numRequests);
  }

  /*//////////////////////////////////////////////////////////////
                                 OWNER
    //////////////////////////////////////////////////////////////*/

  ///@notice sets the addres of fyde contract
  ///@param _fyde address of fyde
  function setFyde(address _fyde) external onlyOwner {
    fyde = IFyde(_fyde);
  }

  ///@notice Set the oracle module
  function setOracleModule(address _oracleModule) external onlyOwner {
    oracleModule = IOracle(_oracleModule);
  }

  ///@notice Change the deviation threshold
  ///@dev 50 = 0.5 % of deviation
  function updateDeviationThreshold(uint16 _threshold) public onlyOwner {
    // We bound the threshold between 0.1 % to 10%
    if (_threshold < 10 || _threshold > 1000) revert ValueOutOfBounds();
    deviationThreshold = _threshold;
  }

  ///@notice Update the gas usage for a specific relayer action
  ///@param _relayerAction hash of the relayer action
  ///@param _gasUsage gas usage
  function updateGasUsageToAction(bytes32 _relayerAction, uint256 _gasUsage) public onlyOwner {
    actionToGasUsage[_relayerAction] = _gasUsage;
  }

  ///@notice Collect ETH from the contract, this for paying keeper services
  function collectEth(address payable _recipient) external onlyOwner {
    uint256 ethBalance = address(this).balance;
    (bool sent,) = _recipient.call{value: ethBalance}("");
    require(sent);
  }

  /*//////////////////////////////////////////////////////////////
                            EXT USER ENTRY POINT
    //////////////////////////////////////////////////////////////*/

  ///@notice Entry function for requesting a deposit, can be a standard deposit or a governance
  /// deposit
  ///@param _userRequest struct containing data
  ///@param _keepGovRights If true make a governance
  ///@param _minTRSYExpected Slippage parameter ensuring minimum amout of TRSY to be received
  ///@dev User have to forward some eth paying for the keeper
  ///     Once the function is called the request is stack on the queue
  function requestDeposit(
    UserRequest[] calldata _userRequest,
    bool _keepGovRights,
    uint256 _minTRSYExpected
  ) external payable whenNotPaused onlyUser {
    bytes32 actionHash = _keepGovRights
      ? keccak256(abi.encodePacked("Deposit_Governance_", _uint2str(_userRequest.length)))
      : keccak256(abi.encodePacked("Deposit_Standard_", _uint2str(_userRequest.length)));

    uint256 gasToForward = actionToGasUsage[actionHash];
    if (_keepGovRights) {
      bool userHasProxy = GOVERNANCE_MODULE.userToProxy(msg.sender) == address(0x0) ? false : true;
      gasToForward = userHasProxy
        ? gasToForward
        : actionToGasUsage[actionHash]
          + actionToGasUsage[keccak256(abi.encodePacked("Proxy_Creation"))];
    }

    gasToForward = gasToForward * oracleModule.getGweiPrice();

    if (msg.value < gasToForward) revert NotEnoughGas(msg.value, gasToForward);

    _checkNumberOfAsset(_userRequest.length);

    address[] memory assetIn = new address[](_userRequest.length);
    uint256[] memory amountIn = new uint256[](_userRequest.length);

    for (uint256 i; i < _userRequest.length; i++) {
      // Checks zero inputs
      _checkZeroValue(_userRequest[i].amount);
      _checkZeroAddress(_userRequest[i].asset);

      // Unpack data
      assetIn[i] = _userRequest[i].asset;
      amountIn[i] = _userRequest[i].amount;
    }

    // Length assetIn == amountIn
    _checkForConsistentLength(assetIn, amountIn);
    // Check not double assets
    _checkForDuplicates(assetIn);

    // Checks if asset is supported and not quarantined
    _checkIsSupported(assetIn);
    _checkIsNotQuarantined(assetIn);
    if (_keepGovRights) _checkIsAllowedInGov(assetIn);

    // Put request in queue, for deposits, assetOut and amountOut are set to empty array
    RequestData memory request = RequestData({
      // Id is only used for events purpose
      id: nonce,
      requestor: msg.sender,
      assetIn: assetIn,
      amountIn: amountIn,
      assetOut: new address[](0),
      amountOut: new uint256[](0),
      keepGovRights: _keepGovRights,
      slippageChecker: _minTRSYExpected
    });
    _enqueueRequest(request);

    emit DepositRequested(nonce, request);
    nonce++;
  }

  ///@notice Entry function for requesting a standard
  ///@param _userRequest struct containing data
  ///@param _maxTRSYToPay Slippage parameter ensure maximum amout of TRSY willing to pay
  ///@dev User have to forward some eth paying for the keeper
  ///     Once the function is called the request is stack on the queue
  function requestWithdraw(UserRequest[] calldata _userRequest, uint256 _maxTRSYToPay)
    external
    payable
    whenNotPaused
    onlyUser
  {
    bytes32 actionHash =
      keccak256(abi.encodePacked("Withdraw_Standard_", _uint2str(_userRequest.length)));
    uint256 gasToForward = actionToGasUsage[actionHash] * oracleModule.getGweiPrice();

    if (msg.value < gasToForward) revert NotEnoughGas(msg.value, gasToForward);

    _checkNumberOfAsset(_userRequest.length);

    address[] memory assetOut = new address[](_userRequest.length);
    uint256[] memory amountOut = new uint256[](_userRequest.length);

    for (uint256 i; i < _userRequest.length; i++) {
      // Checks zero inputs
      _checkZeroValue(_userRequest[i].amount);
      _checkZeroAddress(_userRequest[i].asset);

      assetOut[i] = _userRequest[i].asset;
      amountOut[i] = _userRequest[i].amount;
    }
    // Check not double assets
    _checkForDuplicates(assetOut);

    // Length assetIn == amountIn
    _checkForConsistentLength(assetOut, amountOut);

    // Checks asset is supported in Fyde
    _checkIsNotQuarantined(assetOut);
    _checkIsSupported(assetOut);

    // put request in queue, for withdraw, assetIn and amountIn are set to empty array
    RequestData memory request = RequestData({
      id: nonce,
      requestor: msg.sender,
      assetIn: new address[](0),
      amountIn: new uint256[](0),
      assetOut: assetOut,
      amountOut: amountOut,
      keepGovRights: false,
      slippageChecker: _maxTRSYToPay
    });

    _enqueueRequest(request);

    emit WithdrawRequested(nonce, request);
    nonce++;
  }

  ///@notice Function used by user to make a (single-token) withdrawal from their governance proxy
  ///@param _userRequest struct containing data
  ///@param _user address of user who makes the withdraw
  ///@param _maxTRSYToPay maximum amout of stTRSY willing to pay, otherwise withdraw reverts
  ///@dev This function create a request that will be process by the keeper
  ///@dev owner of fyde can force withdraw for other users
  function requestGovernanceWithdraw(
    UserRequest memory _userRequest,
    address _user,
    uint256 _maxTRSYToPay
  ) external payable whenNotPaused onlyUser {
    bytes32 actionHash = keccak256(abi.encodePacked("Withdraw_Governance_1"));
    uint256 gasToForward = actionToGasUsage[actionHash] * oracleModule.getGweiPrice();

    if (msg.value < gasToForward) revert NotEnoughGas(msg.value, gasToForward);

    if (msg.sender != _user && msg.sender != owner) revert Unauthorized();

    address[] memory assetOut = new address[](1);
    uint256[] memory amountOut = new uint256[](1);

    // Checks zero inputs
    _checkZeroValue(_userRequest.amount);
    _checkZeroAddress(_userRequest.asset);

    assetOut[0] = _userRequest.asset;
    amountOut[0] = _userRequest.amount;

    // Checks asset is supported in Fyde
    _checkIsNotQuarantined(assetOut);
    _checkIsSupported(assetOut);

    // put request in queue, for withdraw, assetIn and amountIn are set to empty array
    RequestData memory request = RequestData({
      id: nonce,
      requestor: _user,
      assetIn: new address[](0),
      amountIn: new uint256[](0),
      assetOut: assetOut,
      amountOut: amountOut,
      keepGovRights: true,
      slippageChecker: _maxTRSYToPay
    });

    _enqueueRequest(request);

    emit WithdrawRequested(nonce, request);
    nonce++;
  }

  function getGasToForward(string memory action, bool isGovDeposit) external view returns (uint256) {
    bytes32 actionHash = keccak256(abi.encodePacked(action));
    uint256 gasToForward = actionToGasUsage[actionHash];
    if (isGovDeposit) {
      bool userHasProxy = GOVERNANCE_MODULE.userToProxy(msg.sender) == address(0x0) ? false : true;
      gasToForward = userHasProxy
        ? gasToForward
        : actionToGasUsage[actionHash]
          + actionToGasUsage[keccak256(abi.encodePacked("Proxy_Creation"))];
    }

    gasToForward = gasToForward * oracleModule.getGweiPrice();
    return gasToForward;
  }

  /*//////////////////////////////////////////////////////////////
                               SWAP
    //////////////////////////////////////////////////////////////*/

  function requestSwap(
    address _assetIn,
    uint256 _amountIn,
    address _assetOut,
    uint256 _minAmountOut
  ) external payable whenSwapNotPaused onlySwapper {
    // Gas comsumption
    bytes32 actionHash = keccak256(abi.encodePacked("Swap"));
    uint256 gasToForward = actionToGasUsage[actionHash] * oracleModule.getGweiPrice();

    if (msg.value < gasToForward) revert NotEnoughGas(msg.value, gasToForward);

    // Checks zero inputs
    _checkZeroValue(_amountIn);
    _checkZeroAddress(_assetIn);
    _checkZeroAddress(_assetOut);
    if (_assetIn == _assetOut) revert DuplicatesAssets();

    // Checks if asset is supported and not quarantined
    address[] memory assets = new address[](2);
    assets[0] = _assetIn;
    assets[1] = _assetOut;
    _checkIsSupported(assets);
    _checkIsNotQuarantined(assets);
    _checkIfSwapAllowed(assets);

    address[] memory assetIn = new address[](1);
    uint256[] memory amountIn = new uint256[](1);
    address[] memory assetOut = new address[](1);
    uint256[] memory amountOut = new uint256[](1);

    assetIn[0] = _assetIn;
    amountIn[0] = _amountIn;
    assetOut[0] = _assetOut;

    // put request in queue
    RequestData memory request = RequestData({
      id: nonce,
      requestor: msg.sender,
      assetIn: assetIn,
      amountIn: amountIn,
      assetOut: assetOut,
      amountOut: amountOut,
      keepGovRights: false,
      slippageChecker: _minAmountOut
    });

    _enqueueRequest(request);

    emit SwapRequested(nonce, request);
    nonce++;
  }

  /*//////////////////////////////////////////////////////////////
                            GELATO FUNCTIONS
    //////////////////////////////////////////////////////////////*/

  ///@notice Check if there is pending request
  function checker_processRequest() external view returns (bool, bytes memory) {
    if (getNumPendingRequest() == 0) return (false, bytes("No pending request"));

    uint256 protocolAUM = fyde.computeProtocolAUM();

    return (true, abi.encodeCall(this.processRequests, (protocolAUM)));
  }

  ///@notice Batch process requests in queue
  ///@param _protocolAUM Computed by the keeper off chain
  ///@dev This function is called by the keeper parsing the AUM and will execute both deposits and
  /// withdraws
  function processRequests(uint256 _protocolAUM) external onlyKeeper whenNotPaused {
    uint256 nReq = getNumPendingRequest();
    if (nReq == 0) revert NoRequest();
    nReq = MAX_BATCH_SIZE >= nReq ? nReq : MAX_BATCH_SIZE;

    //collect management fee
    fyde.collectManagementFee();

    uint256 currentAUM = _protocolAUM;

    // Loop over requests and execute
    for (uint256 i; i < nReq; i++) {
      if (getRequest(0).assetIn.length > 0 && getRequest(0).assetOut.length == 0) {
        RequestData memory request = _dequeueRequest();

        // Deposit
        try fyde.processDeposit(currentAUM, request) returns (uint256 usdDeposit) {
          currentAUM += usdDeposit;
          emit ProcessRequestSuccess(request.id);
        } catch (bytes memory reason) {
          emit ProcessRequestFailed(request.id, reason);
        }
      } else if (getRequest(0).assetOut.length > 0 && getRequest(0).assetIn.length == 0) {
        RequestData memory request = _dequeueRequest();

        // Withdraw
        try fyde.processWithdraw(currentAUM, request) returns (uint256 usdWithdraw) {
          currentAUM -= usdWithdraw;
          emit ProcessRequestSuccess(request.id);
        } catch (bytes memory reason) {
          emit ProcessRequestFailed(request.id, reason);
        }
      } else if (getRequest(0).assetIn.length == 1 && getRequest(0).assetOut.length == 1) {
        RequestData memory request = _dequeueRequest();

        // Swap
        try fyde.processSwap(_protocolAUM, request) returns (int256 deltaAUM) {
          if (deltaAUM > 0) currentAUM += uint256(deltaAUM);
          else if (deltaAUM < 0) currentAUM -= uint256(-deltaAUM);
          else currentAUM = currentAUM;
          emit ProcessRequestSuccess(request.id);
        } catch (bytes memory reason) {
          emit ProcessRequestFailed(request.id, reason);
        }
      }
    }
  }

  ///@notice Offchain checker for gelato bot
  function checker_updateProtocolAUM() external view returns (bool, bytes memory) {
    uint256 aum = fyde.getProtocolAUM();
    uint256 nAum = fyde.computeProtocolAUM();

    if (aum == 0 && nAum != 0) return (true, abi.encodeCall(this.updateProtocolAUM, (nAum)));
    // Update if deviation threshold % (0.5 by default)  diff between on-chain and offchain
    if (PercentageMath._isInRange(aum, nAum, deviationThreshold)) {
      return (false, bytes("AUM is in range"));
    } else {
      return (true, abi.encodeCall(this.updateProtocolAUM, (nAum)));
    }
  }

  ///@notice Update the protocol AUM, called by Gelato Bot
  function updateProtocolAUM(uint256 nAum) external onlyKeeper {
    fyde.updateProtocolAUM(nAum);
  }
  /*//////////////////////////////////////////////////////////////
                                 INTERNAL
    //////////////////////////////////////////////////////////////*/

  function _checkNumberOfAsset(uint256 userRequestLength) internal pure {
    if (userRequestLength > MAX_ASSET_TO_REQUEST || userRequestLength == 0) {
      revert IncorrectNumOfAsset();
    }
  }

  function _checkIsSupported(address[] memory _assets) internal view {
    address notSupportedAsset = fyde.isAnyNotSupported(_assets);
    if (notSupportedAsset != address(0x0)) revert AssetNotSupported(notSupportedAsset);
  }

  function _checkIsNotQuarantined(address[] memory _assets) internal view {
    address quarantinedAsset = isAnyQuarantined(_assets);
    if (quarantinedAsset != address(0x0)) revert AssetIsQuarantined(quarantinedAsset);
  }

  function _checkIsAllowedInGov(address[] memory _assets) internal view {
    address notAllowedInGovAsset = GOVERNANCE_MODULE.isAnyNotOnGovWhitelist(_assets);
    if (notAllowedInGovAsset != address(0x0)) {
      revert AssetNotAllowedInGovernancePool(notAllowedInGovAsset);
    }
  }

  function _checkForDuplicates(address[] memory _assetList) internal pure {
    for (uint256 idx; idx < _assetList.length - 1; idx++) {
      for (uint256 idx2 = idx + 1; idx2 < _assetList.length; idx2++) {
        if (_assetList[idx] == _assetList[idx2]) revert DuplicatesAssets();
      }
    }
  }

  function _checkIfSwapAllowed(address[] memory _assets) internal view {
    address notAllowedAsset = fyde.isSwapAllowed(_assets);
    if (notAllowedAsset != address(0x0)) revert SwapDisabled(notAllowedAsset);
  }

  function _uint2str(uint256 _i) internal pure returns (string memory) {
    if (_i == 0) return "0";
    uint256 j = _i;
    uint256 len;
    while (j != 0) {
      len++;
      j /= 10;
    }
    bytes memory bstr = new bytes(len);
    uint256 k = len;
    while (_i != 0) {
      k = k - 1;
      uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
      bytes1 b1 = bytes1(temp);
      bstr[k] = b1;
      _i /= 10;
    }
    return string(bstr);
  }

  modifier whenNotPaused() {
    if (paused) revert ActionPaused();
    _;
  }

  modifier whenSwapNotPaused() {
    if (swapPaused) revert ActionPaused();
    _;
  }
}

File 2 of 41 : Fyde.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

/// Import from Core /////
import {TRSY} from "./core/TRSY.sol";
import {AssetRegistry} from "./core/AssetRegistry.sol";
import {AddressRegistry} from "./core/AddressRegistry.sol";
import {ProtocolState} from "./core/ProtocolState.sol";
import {Tax} from "./core/Tax.sol";
import {GovernanceAccess} from "./core/GovernanceAccess.sol";

/// Structs /////
import {RequestData, ProcessParam, AssetInfo} from "./core/Structs.sol";

/// Utils /////
import {Ownable} from "./utils/Ownable.sol";
import {PercentageMath} from "./utils/PercentageMath.sol";
import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

//Interfaces
import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IOracle} from "src/interfaces/IOracle.sol";
import {IRelayer} from "src/interfaces/IRelayer.sol";

///@title Fyde contract
///@notice Fyde is the main contract of the protocol, it handles logic of deposit and withdraw in
/// the protocol
///        Deposit and withdraw occurs a mint or a burn of TRSY (ERC20 that represent shares of the
/// procotol in USD value)
///        Users can both deposit/withdraw in standard or governance pool
contract Fyde is TRSY, AddressRegistry, ProtocolState, AssetRegistry, GovernanceAccess, Tax {
  using SafeERC20 for IERC20;
  /*//////////////////////////////////////////////////////////////
                            EVENTS
  //////////////////////////////////////////////////////////////*/

  event FeesCollected(address indexed recipient, uint256 trsyFeesCollected);
  event Deposit(uint32 requestId, uint256 trsyPrice, uint256 usdDepositValue, uint256 trsyMinted);
  event Withdraw(uint32 requestId, uint256 trsyPrice, uint256 usdWithdrawValue, uint256 trsyBurned);
  event Swap(uint32 requestId, address assetOut, uint256 amountOut);
  event ManagementFeeCollected(uint256 feeToMint);

  /*//////////////////////////////////////////////////////////////
                              ERROR
  //////////////////////////////////////////////////////////////*/

  error AumNotInRange();
  error OnlyOneUpdatePerBlock();
  error SlippageExceed();
  error FydeBalanceInsufficient();
  error InsufficientTRSYBalance();
  error AssetPriceNotAvailable();
  error SwapAmountNotAvailable();
  error AssetNotSupported(address asset);
  error SwapDisabled(address asset);
  error AssetIsQuarantined(address asset);

  /*//////////////////////////////////////////////////////////////
                            CONSTRUCTOR
  //////////////////////////////////////////////////////////////*/

  constructor(
    address _relayer,
    address _oracleModule,
    address _governanceModule,
    uint16 _maxAumDeviationAllowed,
    uint72 _taxFactor,
    uint72 _managementFee
  ) Ownable(msg.sender) AddressRegistry(_governanceModule, _relayer) {
    oracleModule = IOracle(_oracleModule);
    updateMaxAumDeviationAllowed(_maxAumDeviationAllowed);
    updateTaxFactor(_taxFactor);
    updateManagementFee(_managementFee);
    _updateLastFeeCollectionTime();
  }

  /*//////////////////////////////////////////////////////////////
                                AUTH
  //////////////////////////////////////////////////////////////*/

  ///@notice Collect and send TRSY fees (from tax fees) to an external address
  ///@param _recipient Address to send TRSY fees to
  ///@param _amount Amount of TRSY to send
  function collectFees(address _recipient, uint256 _amount) external onlyOwner {
    _checkZeroAddress(_recipient);
    _checkZeroValue(_amount);
    balanceOf[address(this)] -= _amount;
    balanceOf[_recipient] += _amount;
    emit FeesCollected(_recipient, _amount);
  }

  ///@notice Collect management fee by inflating TRSY and minting to Fyde
  ///        is called by the relayer when processingRequests
  function collectManagementFee() external {
    uint256 feePerSecond = uint256(protocolData.managementFee / 31_557_600);
    uint256 timePeriod = block.timestamp - protocolData.lastFeeCollectionTime;
    if (timePeriod == 0) return;
    uint256 feeToMint = feePerSecond * timePeriod * totalSupply / 1e18;
    _updateLastFeeCollectionTime();
    _mint(address(this), feeToMint);
    emit ManagementFeeCollected(feeToMint);
  }

  /*//////////////////////////////////////////////////////////////
                  RELAYER & KEEPER FUNCTIONS
  //////////////////////////////////////////////////////////////*/

  ///@notice Update protocol AUM, called by keeper
  ///@param _aum New AUM
  ///@dev Can at most be updated by maxDeviationThreshold and only once per block
  function updateProtocolAUM(uint256 _aum) external {
    if (msg.sender != RELAYER && msg.sender != owner) revert Unauthorized();
    if (block.number == protocolData.lastAUMUpdateBlock) revert CoolDownPeriodActive();
    protocolData.lastAUMUpdateBlock = uint48(block.number);
    (, uint256 limitedAum) = _AumIsInRange(_aum);
    _updateProtocolAUM(limitedAum);
  }

  /*//////////////////////////////////////////////////////////////
                  PROCESSING DEPOSIT ACTIONS
  //////////////////////////////////////////////////////////////*/

  ///@notice Process deposit action, called by relayer
  ///@param _protocolAUM AUM given by keeper
  ///@param _req RequestData struct
  ///@return totalUsdDeposit USD value of the deposit
  function processDeposit(uint256 _protocolAUM, RequestData calldata _req)
    external
    onlyRelayer
    returns (uint256)
  {
    // Check if asset is supported
    _checkIsSupported(_req.assetIn);
    _checkIsNotQuarantined(_req.assetIn);

    // is keeper AUM in range
    (bool isInRange,) = _AumIsInRange(_protocolAUM);
    if (!isInRange) revert AumNotInRange();

    (
      ProcessParam[] memory processParam,
      uint256 sharesToMint,
      uint256 taxInTRSY,
      uint256 totalUsdDeposit
    ) = getProcessParamDeposit(_req, _protocolAUM);

    // Slippage checker
    if (_req.slippageChecker > sharesToMint) revert SlippageExceed();

    // Transfer assets to Fyde
    for (uint256 i; i < _req.assetIn.length; i++) {
      IERC20(_req.assetIn[i]).safeTransferFrom(_req.requestor, address(this), _req.amountIn[i]);
    }

    if (_req.keepGovRights) _govDeposit(_req, processParam);
    else _standardDeposit(_req, sharesToMint);

    // Mint tax and keep in contract
    if (taxInTRSY > 0) _mint(address(this), taxInTRSY);
    _updateProtocolAUM(_protocolAUM + totalUsdDeposit);

    uint256 trsyPrice = (1e18 * (_protocolAUM + totalUsdDeposit)) / totalSupply;
    emit Deposit(_req.id, trsyPrice, totalUsdDeposit, sharesToMint);
    return totalUsdDeposit;
  }

  function _standardDeposit(RequestData calldata _req, uint256 _sharesToMint) internal {
    // Accounting
    _increaseAssetTotalAmount(_req.assetIn, _req.amountIn);

    // Minting shares
    _mint(_req.requestor, _sharesToMint);
  }

  function _govDeposit(RequestData calldata _req, ProcessParam[] memory _processParam) internal {
    uint256[] memory sharesAfterTax = new uint256[](_req.assetIn.length);
    uint256[] memory amountInAfterTax = new uint256[](_req.assetIn.length);
    // Same average tax rate is applied to each asset
    uint256 taxMultiplicator;
    uint256 totalTrsy;
    for (uint256 i; i < _req.assetIn.length; i++) {
      taxMultiplicator = 1e18 * _processParam[i].sharesAfterTax / (_processParam[i].sharesBeforeTax);
      amountInAfterTax[i] = _req.amountIn[i] * taxMultiplicator / 1e18;
      sharesAfterTax[i] = _processParam[i].sharesAfterTax;
      totalTrsy += sharesAfterTax[i];
    }

    // Mint stTRSY and transfer token into proxy
    address proxy = GOVERNANCE_MODULE.govDeposit(
      _req.requestor, _req.assetIn, amountInAfterTax, sharesAfterTax, totalTrsy
    );

    for (uint256 i; i < _req.assetIn.length; i++) {
      IERC20(_req.assetIn[i]).safeTransfer(proxy, amountInAfterTax[i]);
    }

    // Accounting
    _increaseAssetTotalAmount(_req.assetIn, _req.amountIn);
    _increaseAssetProxyAmount(_req.assetIn, amountInAfterTax);

    // Mint
    _mint(address(GOVERNANCE_MODULE), totalTrsy);
  }

  /*//////////////////////////////////////////////////////////////
                  PROCESSING WITHDRAW ACTIONS
  //////////////////////////////////////////////////////////////*/

  ///@notice Process withdraw action, called by relayer
  ///@param _protocolAUM AUM given by keeper
  ///@param _req RequestData struct
  ///@return totalUsdWithdraw USD value of the withdraw
  function processWithdraw(uint256 _protocolAUM, RequestData calldata _req)
    external
    onlyRelayer
    returns (uint256)
  {
    // Check if asset is supported
    _checkIsSupported(_req.assetOut);
    _checkIsNotQuarantined(_req.assetOut);

    // is keeper AUM in range
    (bool isInRange,) = _AumIsInRange(_protocolAUM);
    if (!isInRange) revert AumNotInRange();

    uint256 totalUsdWithdraw;
    uint256 totalSharesToBurn;

    (totalUsdWithdraw, totalSharesToBurn) =
      _req.keepGovRights ? _govWithdraw(_protocolAUM, _req) : _standardWithdraw(_protocolAUM, _req);

    // Accounting
    _decreaseAssetTotalAmount(_req.assetOut, _req.amountOut);
    _updateProtocolAUM(_protocolAUM - totalUsdWithdraw);

    // Calculate for offchain purpose
    uint256 trsyPrice =
      totalSupply != 0 ? (1e18 * (_protocolAUM - totalUsdWithdraw)) / totalSupply : 0;
    emit Withdraw(_req.id, trsyPrice, totalUsdWithdraw, totalSharesToBurn);
    return totalUsdWithdraw;
  }

  function _govWithdraw(uint256 _protocolAUM, RequestData calldata _req)
    internal
    returns (uint256, uint256)
  {
    uint256 usdVal = getQuote(_req.assetOut[0], _req.amountOut[0]);

    if (usdVal == 0) revert AssetPriceNotAvailable();

    uint256 trsyToBurn = _convertToShares(usdVal, _protocolAUM);
    if (_req.slippageChecker < trsyToBurn) revert SlippageExceed();

    _burn(address(GOVERNANCE_MODULE), trsyToBurn);

    _decreaseAssetProxyAmount(_req.assetOut, _req.amountOut);

    GOVERNANCE_MODULE.govWithdraw(_req.requestor, _req.assetOut[0], _req.amountOut[0], trsyToBurn);
    IERC20(_req.assetOut[0]).safeTransfer(_req.requestor, _req.amountOut[0]);

    return (usdVal, trsyToBurn);
  }

  function _standardWithdraw(uint256 _protocolAUM, RequestData calldata _req)
    internal
    returns (uint256, uint256)
  {
    // check if requested token are available
    for (uint256 i = 0; i < _req.assetOut.length; i++) {
      if (standardAssetAccounting(_req.assetOut[i]) < _req.amountOut[i]) {
        revert FydeBalanceInsufficient();
      }
    }

    (, uint256 totalSharesToBurn,, uint256 taxInTRSY, uint256 totalUsdWithdraw) =
      getProcessParamWithdraw(_req, _protocolAUM);

    if (totalSharesToBurn > _req.slippageChecker) revert SlippageExceed();

    if (balanceOf[_req.requestor] < totalSharesToBurn) revert InsufficientTRSYBalance();

    _burn(_req.requestor, totalSharesToBurn);

    // Give tax to this contract
    if (taxInTRSY > 0) _mint(address(this), taxInTRSY);

    for (uint256 i = 0; i < _req.assetOut.length; i++) {
      // Send asset to recipient
      IERC20(_req.assetOut[i]).safeTransfer(_req.requestor, _req.amountOut[i]);
    }

    return (totalUsdWithdraw, totalSharesToBurn);
  }

  /*//////////////////////////////////////////////////////////////
                              SWAP
  //////////////////////////////////////////////////////////////*/

  function processSwap(uint256 _protocolAUM, RequestData calldata _req)
    external
    onlyRelayer
    returns (int256)
  {
    // Check if asset is supported
    _checkIsSupported(_req.assetIn);
    _checkIsSupported(_req.assetOut);
    _checkIsNotQuarantined(_req.assetIn);
    _checkIsNotQuarantined(_req.assetOut);
    _checkIfSwapAllowed(_req.assetIn);
    _checkIfSwapAllowed(_req.assetOut);

    // is keeper AUM in range
    (bool isInRange,) = _AumIsInRange(_protocolAUM);
    if (!isInRange) revert AumNotInRange();

    (uint256 amountOut, int256 deltaAUM) =
      getSwapAmountOut(_req.assetIn[0], _req.amountIn[0], _req.assetOut[0], _protocolAUM);
    if (amountOut == 0) revert SwapAmountNotAvailable();

    if (amountOut < _req.slippageChecker) revert SlippageExceed();

    // Check enough asset in protocol
    if (standardAssetAccounting(_req.assetOut[0]) < amountOut) revert FydeBalanceInsufficient();

    // Update AUM
    uint256 aum;
    // If the swapper pays net tax, we mint the corresponding TRSY to fyde. This way TRSY price
    // stays constant
    if (deltaAUM > 0) {
      aum = _protocolAUM + uint256(deltaAUM);
      _mint(address(this), _convertToShares(uint256(deltaAUM), _protocolAUM));
      // If incentives are higher tan taxes, we burn TRSY from fyde, to keep TRSY price constant
      // as backup if not enough TRSY in Fyde, we don't burn, i.e. TRSY price goes down and
      // incentives are
      // paid by pool
      // this way by frequently cashing out TRSY from fyde we can manually decide how much tax to
      // keep for ourselves
      // or leave in Fyde for incentives
    } else if (deltaAUM < 0) {
      aum = _protocolAUM - uint256(-deltaAUM);
      uint256 trsyToBurn = _convertToShares(uint256(-deltaAUM), _protocolAUM);
      trsyToBurn = balanceOf[address(this)] >= trsyToBurn ? trsyToBurn : balanceOf[address(this)];
      if (trsyToBurn != 0) _burn(address(this), trsyToBurn);
    } else {
      aum = _protocolAUM;
    }

    _updateProtocolAUM(aum);

    // Log accounting
    _increaseAssetTotalAmount(_req.assetIn[0], _req.amountIn[0]);
    _decreaseAssetTotalAmount(_req.assetOut[0], amountOut);

    // Transfer asset
    IERC20(_req.assetIn[0]).safeTransferFrom(_req.requestor, address(this), _req.amountIn[0]);
    IERC20(_req.assetOut[0]).safeTransfer(_req.requestor, amountOut);

    emit Swap(_req.id, _req.assetOut[0], amountOut);
    return deltaAUM;
  }

  /*///////////////////////////////////////////////////////////////
                              GETTERS
  //////////////////////////////////////////////////////////////*/

  ///@notice Give a quote for a speficic asset deposit
  ///@param _asset asset address to quote
  ///@param _amount amount of asset to deposit
  ///@return USD value of the specified deposit (return 18 decimals, 1USD = 1e18)
  ///@dev    If price is inconsistent or not available, returns 0 from oracle module -> needs proper
  ///        handling
  function getQuote(address _asset, uint256 _amount) public view override returns (uint256) {
    AssetInfo memory _assetInfo = assetInfo[_asset];
    uint256 price = oracleModule.getPriceInUSD(_asset, _assetInfo);
    return (_amount * price) / (10 ** _assetInfo.assetDecimals);
  }

  ///@notice Get the USD value of an asset in the protocol
  ///@param _asset asset address
  ///@return USD value of the asset
  ///@dev    If price is inconsistent or not available, returns 0 -> needs proper handling
  function getAssetAUM(address _asset) public view returns (uint256) {
    return getQuote(_asset, totalAssetAccounting[_asset]);
  }

  ///@notice Compute the USD AUM for the protocol
  ///@dev Should NOT be call within a contract (GAS EXPENSIVE), called off-chain by keeper
  function computeProtocolAUM() public view returns (uint256) {
    address asset;
    uint256 aum;
    uint256 assetAUM;
    address[] memory nAsset = assetsList;
    uint256 length = nAsset.length;
    for (uint256 i = 0; i < length; ++i) {
      asset = nAsset[i];
      if (totalAssetAccounting[asset] == 0) continue;
      assetAUM = getAssetAUM(asset);
      if (assetAUM == 0) return protocolData.aum;
      aum += assetAUM;
    }
    return aum;
  }

  /*//////////////////////////////////////////////////////////////
                        PROCESS PARAM
  //////////////////////////////////////////////////////////////*/

  ///@notice Return the process param for a deposit
  ///@param _req RequestData struct
  ///@param _protocolAUM AUM given by keeper
  ///@return processParam array of ProcessParam struct
  ///@return sharesToMint amount of shares to mint
  ///@return taxInTRSY amount of tax in TRSY
  ///@return totalUsdDeposit USD value of the depositn
  function getProcessParamDeposit(RequestData memory _req, uint256 _protocolAUM)
    public
    view
    returns (
      ProcessParam[] memory processParam,
      uint256 sharesToMint,
      uint256 taxInTRSY,
      uint256 totalUsdDeposit
    )
  {
    processParam = new ProcessParam[](_req.assetIn.length);

    // Build data struct and compute value of deposit
    for (uint256 i; i < _req.assetIn.length; i++) {
      uint256 usdVal = getQuote(_req.assetIn[i], _req.amountIn[i]);
      if (usdVal == 0) revert AssetPriceNotAvailable();

      processParam[i] = ProcessParam({
        targetConc: assetInfo[_req.assetIn[i]].targetConcentration,
        currentConc: _getAssetConcentration(_req.assetIn[i], _protocolAUM),
        usdValue: usdVal,
        sharesBeforeTax: _convertToShares(usdVal, _protocolAUM),
        taxableAmount: 0,
        taxInUSD: 0,
        sharesAfterTax: 0
      });

      totalUsdDeposit += usdVal;
    }

    for (uint256 i; i < processParam.length; i++) {
      // Get the TaxInUSD
      processParam[i] =
        _getDepositTax(processParam[i], _protocolAUM, totalUsdDeposit, protocolData.taxFactor);

      // Apply tax to the deposit
      processParam[i].sharesAfterTax =
        _convertToShares(processParam[i].usdValue - processParam[i].taxInUSD, _protocolAUM);
      sharesToMint += processParam[i].sharesAfterTax;
      taxInTRSY += processParam[i].sharesBeforeTax - processParam[i].sharesAfterTax;
    }

    return (processParam, sharesToMint, taxInTRSY, totalUsdDeposit);
  }

  ///@notice Return the process param for a withdraw
  ///@param _req RequestData struct
  ///@param _protocolAUM AUM given by keeper
  ///@return processParam array of ProcessParam struct
  ///@return totalSharesToBurn amount of shares to burn
  ///@return sharesToBurnBeforeTax amount of shares to burn before tax
  ///@return taxInTRSY amount of tax in TRSY
  ///@return totalUsdWithdraw USD value of the withdraw
  function getProcessParamWithdraw(RequestData calldata _req, uint256 _protocolAUM)
    public
    view
    returns (
      ProcessParam[] memory processParam,
      uint256 totalSharesToBurn,
      uint256 sharesToBurnBeforeTax,
      uint256 taxInTRSY,
      uint256 totalUsdWithdraw
    )
  {
    processParam = new ProcessParam[](_req.assetOut.length);

    // Build data struct and compute value of deposit
    for (uint256 i; i < _req.assetOut.length; i++) {
      uint256 usdVal = getQuote(_req.assetOut[i], _req.amountOut[i]);
      if (usdVal == 0) revert AssetPriceNotAvailable();

      processParam[i] = ProcessParam({
        targetConc: assetInfo[_req.assetOut[i]].targetConcentration,
        currentConc: _getAssetConcentration(_req.assetOut[i], _protocolAUM),
        usdValue: usdVal,
        sharesBeforeTax: 0,
        taxableAmount: 0,
        taxInUSD: 0,
        sharesAfterTax: 0
      });

      totalUsdWithdraw += usdVal;
    }

    for (uint256 i; i < processParam.length; i++) {
      // Get the TaxInUSD
      processParam[i] =
        _getWithdrawTax(processParam[i], _protocolAUM, totalUsdWithdraw, protocolData.taxFactor);
      taxInTRSY += _convertToShares(processParam[i].taxInUSD, _protocolAUM);
    }

    sharesToBurnBeforeTax = _convertToShares(totalUsdWithdraw, _protocolAUM);
    totalSharesToBurn = sharesToBurnBeforeTax + taxInTRSY;
  }

  ///@notice Return the amountOut for a swap accounting for tax and incentive
  ///@param _assetIn asset address to swap
  ///@param _amountIn amount of asset to swap
  ///@param _assetOut asset address to receive
  ///@param _protocolAUM AUM given by keeper
  function getSwapAmountOut(
    address _assetIn,
    uint256 _amountIn,
    address _assetOut,
    uint256 _protocolAUM
  ) public view returns (uint256, int256) {
    // Scope to avoid stack too deep
    {
      uint256 usdValIn = getQuote(_assetIn, _amountIn);
      uint256 assetOutPrice = getQuote(_assetOut, 10 ** assetInfo[_assetOut].assetDecimals);
      if (usdValIn == 0 || assetOutPrice == 0) return (0, int256(0));
    }

    ProcessParam memory processParamIn = ProcessParam({
      targetConc: assetInfo[_assetIn].targetConcentration,
      currentConc: _getAssetConcentration(_assetIn, _protocolAUM),
      usdValue: getQuote(_assetIn, _amountIn),
      sharesBeforeTax: 0,
      taxableAmount: 0,
      taxInUSD: 0,
      sharesAfterTax: 0
    });

    ProcessParam memory processParamOut = ProcessParam({
      targetConc: assetInfo[_assetOut].targetConcentration,
      currentConc: _getAssetConcentration(_assetOut, _protocolAUM),
      usdValue: 0,
      sharesBeforeTax: 0,
      taxableAmount: 0,
      taxInUSD: 0,
      sharesAfterTax: 0
    });

    uint256 usdAmountOut = _getSwapRate(
      processParamIn,
      processParamOut,
      _protocolAUM,
      protocolData.taxFactor,
      assetInfo[_assetIn].incentiveFactor,
      assetInfo[_assetOut].incentiveFactor
    );

    return (
      1e18 * usdAmountOut / getQuote(_assetOut, 1e18),
      int256(processParamIn.usdValue) - int256(usdAmountOut)
    );
  }

  /*//////////////////////////////////////////////////////////////
                            INTERNAL
  //////////////////////////////////////////////////////////////*/

  ///@notice Return asset concentration with keeper AUM
  ///@param _asset asset address
  ///@param _protocolAUM AUM given by keeper
  ///@return current concentration for an asset
  ///@dev    If price is inconsistent or not available, returns 0 -> needs proper handling
  function _getAssetConcentration(address _asset, uint256 _protocolAUM)
    internal
    view
    returns (uint256)
  {
    // To avoid division by 0
    if (_protocolAUM == 0 && protocolData.aum == 0) return 0;
    return (1e20 * getAssetAUM(_asset)) / _protocolAUM;
  }

  ///@notice Perform the comparison between AUM registry and one given by Keeper, return limited AUM
  /// if out of bounds
  function _AumIsInRange(uint256 _keeperAUM) internal view returns (bool, uint256) {
    uint16 maxAumDeviationAllowed = protocolData.maxAumDeviationAllowed;
    uint256 currAum = protocolData.aum;
    uint256 lowerBound = PercentageMath.percentSub(currAum, maxAumDeviationAllowed);
    uint256 upperBound = PercentageMath.percentAdd(currAum, maxAumDeviationAllowed);
    if (_keeperAUM < lowerBound) return (false, lowerBound);
    if (_keeperAUM > upperBound) return (false, upperBound);
    return (true, _keeperAUM);
  }

  function _checkIsSupported(address[] memory _assets) internal view {
    address notSupportedAsset = isAnyNotSupported(_assets);
    if (notSupportedAsset != address(0x0)) revert AssetNotSupported(notSupportedAsset);
  }

  function _checkIsNotQuarantined(address[] memory _assets) internal view {
    address quarantinedAsset = IRelayer(RELAYER).isAnyQuarantined(_assets);
    if (quarantinedAsset != address(0x0)) revert AssetIsQuarantined(quarantinedAsset);
  }

  function _checkIfSwapAllowed(address[] memory _assets) internal view {
    address notAllowedAsset = isSwapAllowed(_assets);
    if (notAllowedAsset != address(0x0)) revert SwapDisabled(notAllowedAsset);
  }

  /*//////////////////////////////////////////////////////////////
                            MODIFIERS
  //////////////////////////////////////////////////////////////*/

  modifier onlyRelayer() {
    if (msg.sender != RELAYER) revert Unauthorized();
    _;
  }
}

File 3 of 41 : QuarantineList.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.19;

import {AccessControl} from "./AccessControl.sol";

///@title QuarantineList contract
///@notice Handle the logic for the quarantine list
abstract contract QuarantineList is AccessControl {
  /// -----------------------------
  ///         Storage
  /// -----------------------------
  uint256 public min_quarantine_duration = 1 days;
  mapping(address => uint128) public quarantineList;

  /// -----------------------------
  ///         Events
  /// -----------------------------

  event AddedToQuarantine(address asset, uint128 expirationTime);
  event RemovedFromQuarantine(address asset);
  /// -----------------------------
  ///         Errors
  /// -----------------------------

  error AssetIsQuarantined(address asset);
  error AssetIsNotQuarantined(address asset);
  error ShortenedExpiration(uint128 currentExpiration, uint128 expiration);
  error ShortQurantineDuration(uint128 duration);

  /// -----------------------------
  ///         Admin external
  /// -----------------------------

  ///@notice Set the minimum quarantine duration
  ///@param _min_quarantine_duration the new minimum quarantine duration
  function set_min_quarantine_duration(uint256 _min_quarantine_duration) external onlyOwner {
    min_quarantine_duration = _min_quarantine_duration;
  }

  ///@notice Add an asset to the quarantine list
  ///@param _asset the address of the assset to be added to the quarantine list
  ///@param _duration the time (in seconds) that the asset should stay in quarantine
  function addToQuarantine(address _asset, uint128 _duration) external onlyGuard {
    if (_duration < min_quarantine_duration) revert ShortQurantineDuration(_duration);

    // gas savings
    uint128 expiration = uint128(block.timestamp) + _duration;
    uint128 currentExpiration = quarantineList[_asset];

    // the new expiration cannot be before the curent one i.e. expiration cannot be reduced, just
    // extended
    if (expiration <= currentExpiration) revert ShortenedExpiration(currentExpiration, expiration);

    quarantineList[_asset] = expiration;

    emit AddedToQuarantine(_asset, expiration);
  }

  ///@notice Remove an asset from the quarantine list
  ///@param _asset the address of the assset to be removed from the quarantine list
  function removeFromQuarantine(address _asset) external onlyGuard {
    // If the asset has nto been quarantined or the duarion period has expired then revert
    if (quarantineList[_asset] < uint128(block.timestamp)) revert AssetIsNotQuarantined(_asset);

    // just set the duration to zero to remove from quarantine
    quarantineList[_asset] = 0;
    emit RemovedFromQuarantine(_asset);
  }

  /// -----------------------------
  ///    External view functions
  /// -----------------------------

  ///@notice Check if an asset is quarantined
  ///@param _asset the address of the assset to be checked
  function isQuarantined(address _asset) public view returns (bool) {
    return quarantineList[_asset] >= uint128(block.timestamp);
  }

  ///@notice Check if any asset from a given list is quarantined
  ///@param _assets an array of asset addresses that need to be checked
  ///@return address of first quarantined asset or address(0x0) if none quarantined
  function isAnyQuarantined(address[] memory _assets) public view returns (address) {
    for (uint256 i = 0; i < _assets.length;) {
      if (isQuarantined(_assets[i])) return _assets[i];

      unchecked {
        ++i;
      }
    }
    return address(0x0);
  }
}

File 4 of 41 : RequestQueue.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {RequestQ, RequestData} from "./Structs.sol";

///@title RequestQueue contract
///@notice Handle the logic for the request queue
abstract contract RequestQueue {
  /*//////////////////////////////////////////////////////////////
                                 STORAGE
    //////////////////////////////////////////////////////////////*/

  ///@notice Request queue
  RequestQ internal requestQueue;

  /*//////////////////////////////////////////////////////////////
                                 ERROR
    //////////////////////////////////////////////////////////////*/
  error EmptyQueue();
  error NotEnoughRequests();

  /*//////////////////////////////////////////////////////////////
                                 GETTERS
    //////////////////////////////////////////////////////////////*/

  ///@notice Return the request, to get the first request pass 0 as index
  ///@dev return arbitrary requests in queue by providing the desired index e.g. 0 => first element
  /// in queue
  function getRequest(uint64 idx) public view returns (RequestData memory) {
    return requestQueue.requestData[requestQueue.start + idx];
  }

  ///@notice Return the number of pending request
  function getNumPendingRequest() public view returns (uint256) {
    return requestQueue.end - requestQueue.start;
  }

  /*//////////////////////////////////////////////////////////////
                                 INTERNAL
    //////////////////////////////////////////////////////////////*/

  ///@dev Add request to the end of the queue
  ///@param _newRequest: RequestData - data to be added to the queue
  function _enqueueRequest(RequestData memory _newRequest) internal {
    requestQueue.requestData[requestQueue.end++] = _newRequest;
  }

  ///@dev removes and returns request from the beginning of the queue
  function _dequeueRequest() internal returns (RequestData memory) {
    uint64 start = requestQueue.start++;

    if (start == requestQueue.end) revert EmptyQueue();
    RequestData memory firstRequest = requestQueue.requestData[start];
    delete requestQueue.requestData[start];
    return firstRequest;
  }

  ///@dev removes and returns request from the beginning of the queue
  function _deleteRequests(uint64 _numRequests) internal {
    if (_numRequests > getNumPendingRequest()) revert NotEnoughRequests();
    uint64 start = requestQueue.start;
    uint64 end = start + _numRequests;

    while (start < end) {
      delete requestQueue.requestData[start];
      start++;
    }
    requestQueue.start = start;
  }
}

File 5 of 41 : Structs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

struct AssetInfo {
  uint72 targetConcentration;
  address uniswapPool;
  int72 incentiveFactor;
  uint8 assetDecimals;
  uint8 quoteTokenDecimals;
  address uniswapQuoteToken;
  bool isSupported;
}

struct ProtocolData {
  ///@notice Protocol AUM in USD
  uint256 aum;
  ///@notice multiplicator for the tax equation, 100% = 100e18
  uint72 taxFactor;
  ///@notice Max deviation allowed between AUM from keeper and registry
  uint16 maxAumDeviationAllowed; // Default val 200 == 2 %
  ///@notice block number where AUM was last updated
  uint48 lastAUMUpdateBlock;
  ///@notice annual fee on AUM, in % per year 100% = 100e18
  uint72 managementFee;
  ///@notice last block.timestamp when fee was collected
  uint48 lastFeeCollectionTime;
}

struct UserRequest {
  address asset;
  uint256 amount;
}

struct RequestData {
  uint32 id;
  address requestor;
  address[] assetIn;
  uint256[] amountIn;
  address[] assetOut;
  uint256[] amountOut;
  bool keepGovRights;
  uint256 slippageChecker;
}

struct RequestQ {
  uint64 start;
  uint64 end;
  mapping(uint64 => RequestData) requestData;
}

struct ProcessParam {
  uint256 targetConc;
  uint256 currentConc;
  uint256 usdValue;
  uint256 taxableAmount;
  uint256 taxInUSD;
  uint256 sharesBeforeTax;
  uint256 sharesAfterTax;
}

struct RebalanceParam {
  address asset;
  uint256 assetTotalAmount;
  uint256 assetProxyAmount;
  uint256 assetPrice;
  uint256 sTrsyTotalSupply;
  uint256 trsyPrice;
}

File 6 of 41 : BaseChecker.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

abstract contract BaseChecker {
  error ZeroParameter();
  error InconsistentLengths();

  function _checkZeroValue(uint256 val) internal pure {
    if (val == 0) revert ZeroParameter();
  }

  function _checkZeroAddress(address addr) internal pure {
    if (addr == address(0x0)) revert ZeroParameter();
  }

  function _checkForConsistentLength(address[] memory arr1, uint256[] memory arr2) internal pure {
    if (arr1.length != arr2.length) revert InconsistentLengths();
  }
}

File 7 of 41 : Ownable.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;

///@title Ownable contract
/// @notice Simple 2step owner authorization combining solmate and OZ implementation
abstract contract Ownable {
  /*//////////////////////////////////////////////////////////////
                             STORAGE
    //////////////////////////////////////////////////////////////*/

  ///@notice Address of the owner
  address public owner;

  ///@notice Address of the pending owner
  address public pendingOwner;

  /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

  event OwnershipTransferred(address indexed user, address indexed newOner);
  event OwnershipTransferStarted(address indexed user, address indexed newOwner);
  event OwnershipTransferCanceled(address indexed pendingOwner);

  /*//////////////////////////////////////////////////////////////
                                 ERROR
    //////////////////////////////////////////////////////////////*/

  error Unauthorized();

  /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

  constructor(address _owner) {
    owner = _owner;

    emit OwnershipTransferred(address(0), _owner);
  }

  /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

  ///@notice Transfer ownership to a new address
  ///@param newOwner address of the new owner
  ///@dev newOwner have to acceptOwnership
  function transferOwnership(address newOwner) external onlyOwner {
    pendingOwner = newOwner;
    emit OwnershipTransferStarted(msg.sender, pendingOwner);
  }

  ///@notice NewOwner accept the ownership, it transfer the ownership to newOwner
  function acceptOwnership() external {
    if (msg.sender != pendingOwner) revert Unauthorized();
    address oldOwner = owner;
    owner = pendingOwner;
    delete pendingOwner;
    emit OwnershipTransferred(oldOwner, owner);
  }

  ///@notice Cancel the ownership transfer
  function cancelTransferOwnership() external onlyOwner {
    emit OwnershipTransferCanceled(pendingOwner);
    delete pendingOwner;
  }

  modifier onlyOwner() {
    if (msg.sender != owner) revert Unauthorized();
    _;
  }
}

File 8 of 41 : PercentageMath.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

library PercentageMath {
  ///	CONSTANTS ///

  uint256 internal constant PERCENTAGE_FACTOR = 1e4; // 100.00%
  uint256 internal constant HALF_PERCENTAGE_FACTOR = 0.5e4; // 50.00%
  uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;
  uint256 internal constant MAX_UINT256_MINUS_HALF_PERCENTAGE = 2 ** 256 - 1 - 0.5e4;

  /// INTERNAL ///

  ///@notice Check if value are within the range
  function _isInRange(uint256 valA, uint256 valB, uint256 deviationThreshold)
    internal
    pure
    returns (bool)
  {
    uint256 lowerBound = percentSub(valA, deviationThreshold);
    uint256 upperBound = percentAdd(valA, deviationThreshold);
    if (valB < lowerBound || valB > upperBound) return false;
    else return true;
  }

  /// @notice Executes a percentage addition (x * (1 + p)), rounded up.
  /// @param x The value to which to add the percentage.
  /// @param percentage The percentage of the value to add.
  /// @return y The result of the addition.
  function percentAdd(uint256 x, uint256 percentage) internal pure returns (uint256 y) {
    // Must revert if
    // PERCENTAGE_FACTOR + percentage > type(uint256).max
    //     or x * (PERCENTAGE_FACTOR + percentage) + HALF_PERCENTAGE_FACTOR > type(uint256).max
    // <=> percentage > type(uint256).max - PERCENTAGE_FACTOR
    //     or x > (type(uint256).max - HALF_PERCENTAGE_FACTOR) / (PERCENTAGE_FACTOR + percentage)
    // Note: PERCENTAGE_FACTOR + percentage >= PERCENTAGE_FACTOR > 0
    assembly {
      y := add(PERCENTAGE_FACTOR, percentage) // Temporary assignment to save gas.

      if or(
        gt(percentage, sub(MAX_UINT256, PERCENTAGE_FACTOR)),
        gt(x, div(MAX_UINT256_MINUS_HALF_PERCENTAGE, y))
      ) { revert(0, 0) }

      y := div(add(mul(x, y), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
    }
  }

  /// @notice Executes a percentage subtraction (x * (1 - p)), rounded up.
  /// @param x The value to which to subtract the percentage.
  /// @param percentage The percentage of the value to subtract.
  /// @return y The result of the subtraction.
  function percentSub(uint256 x, uint256 percentage) internal pure returns (uint256 y) {
    // Must revert if
    // percentage > PERCENTAGE_FACTOR
    //     or x * (PERCENTAGE_FACTOR - percentage) + HALF_PERCENTAGE_FACTOR > type(uint256).max
    // <=> percentage > PERCENTAGE_FACTOR
    //     or ((PERCENTAGE_FACTOR - percentage) > 0 and x > (type(uint256).max -
    // HALF_PERCENTAGE_FACTOR) / (PERCENTAGE_FACTOR - percentage))
    assembly {
      y := sub(PERCENTAGE_FACTOR, percentage) // Temporary assignment to save gas.

      if or(
        gt(percentage, PERCENTAGE_FACTOR), mul(y, gt(x, div(MAX_UINT256_MINUS_HALF_PERCENTAGE, y)))
      ) { revert(0, 0) }

      y := div(add(mul(x, y), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
    }
  }
}

File 9 of 41 : IFyde.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;

import {RequestData, RebalanceParam, ProcessParam} from "src/core/Structs.sol";

interface IFyde {
  function protocolData() external view returns (uint256, uint72, uint16, uint48, uint72, uint48);

  function isAnyNotSupported(address[] calldata _assets) external view returns (address);

  function isSwapAllowed(address[] calldata _assets) external view returns (address);

  function computeProtocolAUM() external view returns (uint256);

  function getProtocolAUM() external view returns (uint256);

  function updateProtocolAUM(uint256) external;

  function processDeposit(uint256, RequestData calldata) external returns (uint256);

  function processWithdraw(uint256, RequestData calldata) external returns (uint256);

  function totalAssetAccounting(address) external view returns (uint256);

  function proxyAssetAccounting(address) external view returns (uint256);

  function standardAssetAccounting(address) external view returns (uint256);

  function getQuote(address, uint256) external view returns (uint256);

  function getAssetDecimals(address) external view returns (uint8);

  function collectManagementFee() external;

  function processSwap(uint256, RequestData calldata) external returns (int256);

  function getProcessParamDeposit(RequestData memory _req, uint256 _protocolAUM)
    external
    view
    returns (
      ProcessParam[] memory processParam,
      uint256 sharesToMint,
      uint256 taxInTRSY,
      uint256 totalUsdDeposit
    );

  // GOVERNANCE ACCESS FUNCTIONS

  function transferAsset(address _asset, address _recipient, uint256 _amount) external;

  function getRebalanceParams(address _asset) external view returns (RebalanceParam memory);

  function updateAssetProxyAmount(address _asset, uint256 _amount) external;
}

File 10 of 41 : IGovernanceModule.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;

interface IGovernanceModule {
  function fyde() external view returns (address);

  function proxyImplementation() external view returns (address);

  function proxyBalance(address proxy, address asset) external view returns (uint256);

  function strsyBalance(address _user, address _govToken) external view returns (uint256 balance);

  function assetToStrsy(address _asset) external view returns (address);

  function userToProxy(address _user) external view returns (address);

  function proxyToUser(address _proxy) external view returns (address);

  function isOnGovernanceWhitelist(address _asset) external view returns (bool);

  function getAllGovUsers() external view returns (address[] memory);

  function isAnyNotOnGovWhitelist(address[] calldata _assets) external view returns (address);

  function getUserGTAllowance(uint256 _TRSYAmount, address _token) external view returns (uint256);

  function govDeposit(
    address _depositor,
    address[] calldata _govToken,
    uint256[] calldata _amount,
    uint256[] calldata _amountTRSY,
    uint256 _totalTRSY
  ) external returns (address proxy);

  function govWithdraw(
    address _user,
    address _asset,
    uint256 _amountToWithdraw,
    uint256 _trsyToBurn
  ) external;

  function onStrsyTransfer(address sender, address _recipient) external;

  function unstakeGov(uint256 _amount, address _asset) external;

  function rebalanceProxy(address _proxy, address _asset, address[] memory _usersToRebalance)
    external;
}

File 11 of 41 : IOracle.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;

import {AssetInfo} from "../core/Structs.sol";

interface IOracle {
  function getPriceInUSD(address, AssetInfo calldata) external view returns (uint256);

  function getGweiPrice() external view returns (uint256);
}

File 12 of 41 : TRSY.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {ERC20} from "solmate/tokens/ERC20.sol";

///@title TRSY contract
///@notice Handle the logic for minting and burning TRSY shares
abstract contract TRSY is ERC20 {
  /*//////////////////////////////////////////////////////////////
                                 CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

  constructor() ERC20("TRSY", "TRSY", 18) {}

  /*//////////////////////////////////////////////////////////////
                                 INTERNAL
    //////////////////////////////////////////////////////////////*/

  ///@notice Convert the value of deposit into share of the protocol
  ///@param _usdValue usd value of the deposit
  ///@param _usdAUM AUM of the protocol in USD (given by keeper)
  ///@return TSRY share for an USD deposit
  function _convertToShares(uint256 _usdValue, uint256 _usdAUM) internal view returns (uint256) {
    uint256 supply = totalSupply;
    return supply == 0 ? _usdValue : (_usdValue * supply) / _usdAUM;
  }
}

File 13 of 41 : AssetRegistry.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {AssetInfo} from "./Structs.sol";
import {AddressRegistry} from "./AddressRegistry.sol";
import {ProtocolState} from "./ProtocolState.sol";
import {Ownable} from "../utils/Ownable.sol";
import {BaseChecker} from "../utils/BaseChecker.sol";
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IUniswapV3PoolImmutables} from
  "lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol";

import {IRelayer} from "src/interfaces/IRelayer.sol";

///@title AssetRegistry contract
///@notice Handle logic and state for logging informations regarding the assets
abstract contract AssetRegistry is Ownable, BaseChecker, AddressRegistry, ProtocolState {
  /*//////////////////////////////////////////////////////////////
                                 STORAGE
    //////////////////////////////////////////////////////////////*/

  ///@notice Asset list;
  address[] public assetsList;

  ///@notice last block incentiveFactor was updated, safety measure
  uint128 public lastIncentiveUpdateBlock;
  int128 public incentiveCap;

  ///@notice Map asset address to struct containing info
  mapping(address => AssetInfo) public assetInfo;

  /*//////////////////////////////////////////////////////////////
                                 ERROR
    //////////////////////////////////////////////////////////////*/

  error PoolNotValid();
  error AssetSupported(address asset);
  error NotNormalized();
  error NotZero();
  error CoolDownPeriodActive();

  /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

  event AssetAdded(address _asset);
  event IncentiveFactorUpdated(address indexed asset, int72 incentiveFactor);
  event TargetConcentrationsUpdated();
  event UniswapPoolUpdated(address indexed asset, address uniswapPool);
  event AssetRemoved(address indexed asset);

  /*//////////////////////////////////////////////////////////////
                                 ADD ASSETS
    //////////////////////////////////////////////////////////////*/

  ///@notice Add assets in batch
  ///@param _assets Array of assets to add
  ///@param _uniswapPools Array of address of uniswap Pool
  ///@dev   only uniswap pool and incentive factor are relevant in assetsInfo, the rest is retrieved
  /// on-chain
  function addAssets(address[] calldata _assets, address[] calldata _uniswapPools)
    external
    onlyOwner
  {
    if (_assets.length != _uniswapPools.length) revert InconsistentLengths();

    for (uint256 i; i < _assets.length; ++i) {
      _addAsset(_assets[i], _uniswapPools[i]);
    }
  }

  function _addAsset(address _asset, address _uniswapPool) private {
    if (assetInfo[_asset].isSupported) revert AssetSupported(_asset);

    assetInfo[_asset].isSupported = true;
    assetInfo[_asset].assetDecimals = IERC20Metadata(_asset).decimals();

    setUniswapPool(_asset, _uniswapPool);

    assetsList.push(_asset);

    emit AssetAdded(_asset);
  }

  ///@notice Removes asset from the protocol whitelist
  ///@param _assetIdx index of the asset in the assets array
  ///@dev only possible if there are no tokens of the asset held by the protocol anymore
  function removeAsset(uint256 _assetIdx) external onlyOwner {
    address asset = assetsList[_assetIdx];
    if (totalAssetAccounting[asset] != 0) revert NotZero();
    if (assetInfo[asset].targetConcentration != 0) revert NotZero();
    delete assetInfo[asset];
    assetsList[_assetIdx] = assetsList[assetsList.length - 1];
    assetsList.pop();
    emit AssetRemoved(asset);
  }
  /*//////////////////////////////////////////////////////////////
                                 SETTER
    //////////////////////////////////////////////////////////////*/

  ///@notice Set target concentration for all asset
  ///@param _targetConcentrations Target concentration (1e18 -> 1%)
  ///@dev 1e18 = 1%
  ///@dev targetConcentrations must have same length as assetsList -> can only update all conc at
  // once to enforce normalization
  function setTargetConcentrations(uint72[] calldata _targetConcentrations) external onlyOwner {
    if (_targetConcentrations.length != assetsList.length) revert InconsistentLengths();
    uint72 sum;
    for (uint256 i; i < _targetConcentrations.length; i++) {
      sum += _targetConcentrations[i];
    }
    if (sum > (1e20 + 1e10) || sum < (1e20 - 1e10)) revert NotNormalized();

    for (uint256 i; i < _targetConcentrations.length; i++) {
      assetInfo[assetsList[i]].targetConcentration = _targetConcentrations[i];
    }
    emit TargetConcentrationsUpdated();
  }

  ///@notice Set target concentration for an asset
  ///@param _asset Asset address
  ///@param _incentiveFactor IncentiveFactor (1e18 -> 1%)
  ///@dev 1e18 = 1%, max incentiveCap
  ///@dev Can only be called every 5 blocks, safety measure in case of compromised IncentiveManager
  function setIncentiveFactor(address _asset, int72 _incentiveFactor) external onlyIncentiveManager {
    if (int128(_incentiveFactor) > incentiveCap) revert ValueOutOfBounds();
    if (block.number < uint256(lastIncentiveUpdateBlock) + 5) revert CoolDownPeriodActive();
    lastIncentiveUpdateBlock = uint128(block.number);
    assetInfo[_asset].incentiveFactor = _incentiveFactor;
    emit IncentiveFactorUpdated(_asset, _incentiveFactor);
  }

  ///@notice Set maximum incentive factor
  ///@param _incentiveCap maximum incentive factor
  ///@dev Capped at 1e20 == 100%
  function setIncentiveCap(int128 _incentiveCap) external onlyOwner {
    if (_incentiveCap > 1e20) revert ValueOutOfBounds();
    incentiveCap = _incentiveCap;
  }

  ///@notice Set uniswap pool for an asset
  ///@param _asset Asset address
  ///@param _uniswapPool Uniswap pool address
  function setUniswapPool(address _asset, address _uniswapPool) public onlyOwner {
    if (_uniswapPool == address(0x0)) {
      assetInfo[_asset].uniswapPool = _uniswapPool;
    } else {
      address token0 = IUniswapV3PoolImmutables(_uniswapPool).token0();
      address token1 = IUniswapV3PoolImmutables(_uniswapPool).token1();
      address quoteToken;

      if (token0 == _asset) quoteToken = token1;
      else if (token1 == _asset) quoteToken = token0;
      else revert PoolNotValid();

      assetInfo[_asset].uniswapPool = _uniswapPool;
      assetInfo[_asset].uniswapQuoteToken = quoteToken;
      assetInfo[_asset].quoteTokenDecimals = IERC20Metadata(quoteToken).decimals();
    }
    emit UniswapPoolUpdated(_asset, _uniswapPool);
  }

  /*//////////////////////////////////////////////////////////////
                                GETTER
    //////////////////////////////////////////////////////////////*/

  ///@notice Get isSupported for an asset
  ///@param _assets asset addresses
  ///@return address of first not supported asset or address(0x0) if all supported
  function isAnyNotSupported(address[] memory _assets) public view returns (address) {
    for (uint256 i; i < _assets.length; i++) {
      if (!assetInfo[_assets[i]].isSupported) return _assets[i];
    }
    return address(0x0);
  }

  ///@notice Get isSwapAllowed for an asset array
  ///@param _assets asset addresses
  ///@return address of first not supported asset or address(0x0) if all supported
  function isSwapAllowed(address[] memory _assets) public view returns (address) {
    for (uint256 i; i < _assets.length; i++) {
      if (assetInfo[_assets[i]].incentiveFactor == -100e18) return _assets[i];
    }
    return address(0x0);
  }

  ///@notice Get number of asset decimals
  ///@param _asset Asset address
  ///@return number of decimals
  function getAssetDecimals(address _asset) external view returns (uint8) {
    return assetInfo[_asset].assetDecimals;
  }

  ///@notice Get number of assets in protocol
  function getAssetsListLength() public view returns (uint256) {
    return assetsList.length;
  }

  ///@dev caller has to be whitelisted manager on relayer
  modifier onlyIncentiveManager() {
    if (!IRelayer(RELAYER).isIncentiveManager(msg.sender)) revert Unauthorized();
    _;
  }
}

File 14 of 41 : AddressRegistry.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

// Utils
import {Ownable} from "../utils/Ownable.sol";

// Interfaces
import {IOracle} from "../interfaces/IOracle.sol";
import {IGovernanceModule} from "../interfaces/IGovernanceModule.sol";

///@title AddressRegistry contract
///@notice Handle state and logic for external authorized call (mainly keeper) and the oracle module
abstract contract AddressRegistry is Ownable {
  /*//////////////////////////////////////////////////////////////
                            STORAGE
  //////////////////////////////////////////////////////////////*/

  ///@notice Address of the oracle module
  IOracle public oracleModule;

  ///@notice Governance Registry contract address interface
  IGovernanceModule public immutable GOVERNANCE_MODULE;

  address public RELAYER;

  /*//////////////////////////////////////////////////////////////
                            EVENTS
  //////////////////////////////////////////////////////////////*/

  event OracleModuleUpdated(address indexed oracleModule);

  /*//////////////////////////////////////////////////////////////
                        CONSTRUCTOR
  //////////////////////////////////////////////////////////////*/

  constructor(address _govModule, address _relayer) {
    GOVERNANCE_MODULE = IGovernanceModule(_govModule);
    RELAYER = _relayer;
  }

  /*//////////////////////////////////////////////////////////////
                            ACCESS
  //////////////////////////////////////////////////////////////*/

  function setRelayer(address _relayer) external onlyOwner {
    RELAYER = _relayer;
  }

  ///@notice Set the oracle address
  function setOracleModule(address _oracle) public onlyOwner {
    oracleModule = IOracle(_oracle);
    emit OracleModuleUpdated(_oracle);
  }
}

File 15 of 41 : ProtocolState.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {ProtocolData} from "./Structs.sol";
import {Ownable} from "../utils/Ownable.sol";

///@title ProtocolState contract
///@notice Protocol data storage
abstract contract ProtocolState is Ownable {
  /*//////////////////////////////////////////////////////////////
                                 STORAGE
    //////////////////////////////////////////////////////////////*/

  ///@notice Protocol data
  ProtocolData public protocolData;

  ///@notice Number of token in the protocol
  mapping(address => uint256) public totalAssetAccounting;

  ///@notice Number of token in the proxy
  mapping(address => uint256) public proxyAssetAccounting;

  /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

  error ValueOutOfBounds();

  /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

  event ProtocolAumUpdated(uint256);
  event MaxAumDeviationAllowedUpdated(uint16);
  event TaxFactorUpdated(uint72);
  event ManagementFeeUpdated(uint72);

  /*//////////////////////////////////////////////////////////////
                                 GETTER
    //////////////////////////////////////////////////////////////*/

  ///@notice Get number of token in the standard pool
  ///@param _asset asset address
  ///@return number of token in standard pool
  function standardAssetAccounting(address _asset) public view returns (uint256) {
    return totalAssetAccounting[_asset] - proxyAssetAccounting[_asset];
  }

  ///@notice Get protocolAUM in USD
  ///@return protocol AUM
  function getProtocolAUM() external view returns (uint256) {
    return protocolData.aum;
  }

  /*//////////////////////////////////////////////////////////////
                                 SETTER
    //////////////////////////////////////////////////////////////*/

  ///@notice Change the AUM's comparison deviation threshold
  ///@param threshold new threshold
  ///@dev 200 = 2 % of deviation
  function updateMaxAumDeviationAllowed(uint16 threshold) public onlyOwner {
    // We bound the threshold to 0.1 % to 5%
    if (threshold < 10 || threshold > 500) revert ValueOutOfBounds();
    protocolData.maxAumDeviationAllowed = threshold;
    emit MaxAumDeviationAllowedUpdated(threshold);
  }

  ///@notice set the tax factor
  ///@param _taxFactor new tax factor
  ///@dev 100% = 100e18
  function updateTaxFactor(uint72 _taxFactor) public onlyOwner {
    if (_taxFactor > 100e18) revert ValueOutOfBounds();
    protocolData.taxFactor = _taxFactor;
    emit TaxFactorUpdated(_taxFactor);
  }

  ///@notice change annual management fee
  ///@param _annualFee new annual fee
  ///@dev 100% = 1e18
  function updateManagementFee(uint72 _annualFee) public onlyOwner {
    // We bound the fee to 0 % to 5%
    if (_annualFee > 5e16) revert ValueOutOfBounds();
    protocolData.managementFee = _annualFee;
    emit ManagementFeeUpdated(_annualFee);
  }

  ///@notice Update last fee collection time to current timestamp
  function _updateLastFeeCollectionTime() internal {
    protocolData.lastFeeCollectionTime = uint48(block.timestamp);
  }

  ///@notice Update the protocol AUM
  function _updateProtocolAUM(uint256 _aum) internal {
    protocolData.aum = _aum;
    emit ProtocolAumUpdated(_aum);
  }

  function _increaseAssetTotalAmount(address[] memory _assets, uint256[] memory _amounts) internal {
    for (uint256 i; i < _assets.length; i++) {
      _increaseAssetTotalAmount(_assets[i], _amounts[i]);
    }
  }

  function _increaseAssetTotalAmount(address _asset, uint256 _amount) internal {
    totalAssetAccounting[_asset] += _amount;
  }

  function _increaseAssetProxyAmount(address[] memory _assets, uint256[] memory _amounts) internal {
    for (uint256 i; i < _assets.length; i++) {
      proxyAssetAccounting[_assets[i]] += _amounts[i];
    }
  }

  function _decreaseAssetTotalAmount(address[] memory _assets, uint256[] memory _amounts) internal {
    for (uint256 i; i < _assets.length; i++) {
      _decreaseAssetTotalAmount(_assets[i], _amounts[i]);
    }
  }

  function _decreaseAssetTotalAmount(address _asset, uint256 _amount) internal {
    totalAssetAccounting[_asset] -= _amount;
  }

  function _decreaseAssetProxyAmount(address[] memory _assets, uint256[] memory _amounts) internal {
    for (uint256 i; i < _assets.length; i++) {
      proxyAssetAccounting[_assets[i]] -= _amounts[i];
    }
  }
}

File 16 of 41 : Tax.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {ProcessParam} from "./Structs.sol";
import {MathUtil} from "../utils/MathUtil.sol";
import "synthetix-v3/utils/core-contracts/contracts/utils/SafeCast.sol";

///@title Tax contract
///@notice Handle tax logic, for either a deposit or withdraw compute if the action is unabalacing
/// the protocol
///        if this is the case, the protocol will compute a tax applied on deposit or withdraw by
/// reducing the number of shares to mint
///        or by increasing the number of shares to burn. This tax is then minted to fyde contract.
/// The main logic is that for each action, we compute a taxable amount which is the amount that
/// unbalance the protocol for a given deposit or withdraw,
/// then we apply the tax on this taxable amount.
/// For the swap the logic is the same, we compute the tax and incentive for assetIn and that give
/// the value of assetOut,
/// for this value we compute the tax and incentive for assetOut.
/// SwapRate can be greater for assetOut in case there is no tax and some incentives, the same if no
/// tax no incentive, or lower if there is tax and no incentive.
abstract contract Tax {
  using SafeCastU256 for uint256;
  using SafeCastI256 for int256;

  /*//////////////////////////////////////////////////////////////
                                 MAIN
    //////////////////////////////////////////////////////////////*/

  function _getDepositTax(
    ProcessParam memory processParam,
    uint256 protocolAUM,
    uint256 totalUsdDeposit,
    uint256 taxFactor
  ) internal pure returns (ProcessParam memory) {
    if (processParam.targetConc == 0) {
      processParam.taxInUSD = processParam.usdValue;
      return processParam;
    }
    if (taxFactor == 0) return processParam;
    processParam = _computeDepositTaxableAmount(processParam, protocolAUM, totalUsdDeposit);
    if (processParam.taxableAmount == 0) return processParam;
    processParam = _computeDepositTaxInUSD(processParam, protocolAUM, totalUsdDeposit, taxFactor);
    return processParam;
  }

  function _getWithdrawTax(
    ProcessParam memory processParam,
    uint256 protocolAUM,
    uint256 totalUsdWithdraw,
    uint256 taxFactor
  ) internal pure returns (ProcessParam memory) {
    if (taxFactor == 0) return processParam;
    processParam = _computeWithdrawTaxableAmount(processParam, protocolAUM, totalUsdWithdraw);
    if (processParam.taxableAmount == 0) return processParam;
    processParam = _computeWithdrawTaxInUSD(processParam, protocolAUM, totalUsdWithdraw, taxFactor);
    return processParam;
  }

  function _getSwapRate(
    ProcessParam memory processParamIn,
    ProcessParam memory processParamOut,
    uint256 protocolAUM,
    uint256 taxFactor,
    int256 incentiveFactorIn,
    int256 incentiveFactorOut
  ) internal pure returns (uint256) {
    // Compute tax on deposit
    processParamIn = _getDepositTax(processParamIn, protocolAUM, 0, taxFactor);

    int256 valIn = incentiveFactorIn
      * int256(processParamIn.usdValue - processParamIn.taxableAmount) / int256(1e20);

    // usdValue adjusted with potential tax and incentive
    uint256 withdrawValOut = valIn >= 0
      ? processParamIn.usdValue - processParamIn.taxInUSD + valIn.toUint()
      : processParamIn.usdValue - processParamIn.taxInUSD - (-1 * valIn).toUint();

    processParamOut.usdValue = withdrawValOut;
    processParamOut = _getWithdrawTax(processParamOut, protocolAUM, 0, taxFactor);

    // usdValueOut adjusted with potential tax and incentive
    int256 valOut =
      incentiveFactorOut * int256(withdrawValOut - processParamOut.taxableAmount) / 1e20;

    uint256 usdValOut = valOut >= 0
      ? withdrawValOut - processParamOut.taxInUSD + valOut.toUint()
      : withdrawValOut - processParamOut.taxInUSD - (-1 * valOut).toUint();

    return usdValOut;
  }

  /*//////////////////////////////////////////////////////////////
                                 DEPOSIT
    //////////////////////////////////////////////////////////////*/
  function _computeDepositTaxableAmount(
    ProcessParam memory processParam,
    uint256 protocolAUM,
    uint256 totalUsdDeposit
  ) internal pure returns (ProcessParam memory) {
    int256 deltaConc = protocolAUM.toInt()
      * (processParam.currentConc.toInt() - processParam.targetConc.toInt()) / 1e20;
    int256 targetDeposit = totalUsdDeposit != 0
      ? processParam.targetConc.toInt() * totalUsdDeposit.toInt() / 1e20
      : int256(0);
    int256 tax = processParam.usdValue.toInt() + deltaConc - targetDeposit;
    processParam.taxableAmount =
      MathUtil.min(processParam.usdValue.toInt(), MathUtil.max(tax, int256(0))).toUint();
    return processParam;
  }

  function _computeDepositTaxInUSD(
    ProcessParam memory processParam,
    uint256 protocolAUM,
    uint256 totalUsdDeposit,
    uint256 taxFactor
  ) internal pure returns (ProcessParam memory) {
    uint256 numerator = (protocolAUM * processParam.currentConc / 1e20) + processParam.usdValue;
    uint256 denominator = (protocolAUM + totalUsdDeposit) * processParam.targetConc / 1e20;
    uint256 eq = (1e18 * numerator / denominator) - 1e18;
    uint256 tmpRes = MathUtil.min(eq, 1e18);
    uint256 taxPerc = taxFactor * tmpRes / 1e20; // 1e20 for applying expressing tax as a percentage
    processParam.taxInUSD = processParam.taxableAmount * taxPerc / 1e18;
    return processParam;
  }

  /*//////////////////////////////////////////////////////////////
                                 WITHDRAW
    //////////////////////////////////////////////////////////////*/
  function _computeWithdrawTaxableAmount(
    ProcessParam memory processParam,
    uint256 protocolAUM,
    uint256 totalUsdWithdraw
  ) internal pure returns (ProcessParam memory) {
    int256 deltaConc = protocolAUM.toInt()
      * (processParam.currentConc.toInt() - processParam.targetConc.toInt()) / 1e20;
    int256 targetDeposit = processParam.targetConc.toInt() * totalUsdWithdraw.toInt() / 1e20;
    int256 tax = processParam.usdValue.toInt() - deltaConc - targetDeposit;
    processParam.taxableAmount =
      MathUtil.min(processParam.usdValue.toInt(), MathUtil.max(tax, int256(0))).toUint();
    return processParam;
  }

  function _computeWithdrawTaxInUSD(
    ProcessParam memory processParam,
    uint256 protocolAUM,
    uint256 totalUsdWithdraw,
    uint256 taxFactor
  ) internal pure returns (ProcessParam memory) {
    int256 numerator =
      protocolAUM.toInt() * processParam.currentConc.toInt() / 1e20 - processParam.usdValue.toInt();
    int256 denominator =
      processParam.targetConc.toInt() * (protocolAUM.toInt() - totalUsdWithdraw.toInt()) / 1e20;
    int256 tmpRes = 1e18 - (1e18 * numerator / denominator);
    uint256 tmpRes2 = MathUtil.min(tmpRes.toUint(), 1e18);
    uint256 taxPerc = taxFactor * tmpRes2 / 1e20; // 1e20 for applying expressing tax as a
      // percentage
    processParam.taxInUSD = processParam.taxableAmount * taxPerc / 1e18;
    return processParam;
  }
}

File 17 of 41 : GovernanceAccess.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

/// Import from Core /////
import {AddressRegistry} from "./AddressRegistry.sol";
import {AssetRegistry} from "./AssetRegistry.sol";
import {ProtocolState} from "./ProtocolState.sol";
import {TRSY} from "./TRSY.sol";

/// Structs /////
import {RebalanceParam} from "./Structs.sol";

// Interface
import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

///@title GovernanceAccess contract
///@notice Exposes functions for the governance module
abstract contract GovernanceAccess is TRSY, AddressRegistry, ProtocolState, AssetRegistry {
  using SafeERC20 for IERC20;

  ///@notice Called by the governance module in order to transfer asset when proxy is funded
  ///@param _asset asset address
  ///@param _recipient user address
  ///@param _amount number of token
  function transferAsset(address _asset, address _recipient, uint256 _amount)
    external
    onlyGovernance
  {
    IERC20(_asset).safeTransfer(_recipient, _amount);
  }

  ///@notice Called by the governance module in order to update proxy Accounting after rebalancing
  ///@param _asset asset address
  ///@param _amount number of token
  function updateAssetProxyAmount(address _asset, uint256 _amount) external onlyGovernance {
    proxyAssetAccounting[_asset] = _amount;
  }

  ///@notice Called by the governance module in order get all variables for rebalancing in one call
  /// (save gas)
  ///@param _asset asset address
  ///@return RebalanceParam struct with rebalance parameters
  function getRebalanceParams(address _asset) external view returns (RebalanceParam memory) {
    return RebalanceParam(
      _asset,
      totalAssetAccounting[_asset],
      proxyAssetAccounting[_asset],
      getQuote(_asset, 1e18),
      0,
      1e18 * protocolData.aum / totalSupply
    );
  }

  function getQuote(address _asset, uint256 _amount) public view virtual returns (uint256);

  modifier onlyGovernance() {
    if (msg.sender != address(GOVERNANCE_MODULE)) revert Unauthorized();
    _;
  }
}

File 18 of 41 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 19 of 41 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 20 of 41 : IRelayer.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;

import {RequestData, UserRequest} from "../core/Structs.sol";

interface IRelayer {
  function getNumPendingRequest() external view returns (uint256);

  function getRequest(uint64 idx) external view returns (RequestData memory);

  function requestGovernanceWithdraw(
    UserRequest memory _userRequest,
    address _user,
    uint256 _maxTRSYToPay
  ) external payable;

  function requestWithdraw(UserRequest[] memory _userRequest, uint256 _maxTRSYToPay)
    external
    payable;

  function requestDeposit(
    UserRequest[] memory _userRequest,
    bool _keepGovRights,
    uint256 _minTRSYExpected
  ) external payable;

  function requestSwap(
    address _assetIn,
    uint256 _amountIn,
    address _assetOut,
    uint256 _minAmountOut
  ) external payable;

  function processRequests(uint256 _protocolAUM) external;

  function isQuarantined(address _asset) external view returns (bool);

  function isIncentiveManager(address _incentiveManager) external view returns (bool);

  function MAX_ASSET_TO_REQUEST() external view returns (uint8);

  function actionToGasUsage(bytes32 _actionHash) external view returns (uint256);

  function isUser(address _asset) external view returns (bool);

  function isAnyQuarantined(address[] memory _assets) external view returns (address);
}

File 21 of 41 : AccessControl.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {Ownable} from "src/utils/Ownable.sol";

///@title AccessControl
/// @notice Handles the different access authorizations for the relayer
abstract contract AccessControl is Ownable {
  /*//////////////////////////////////////////////////////////////
                             STORAGE
    //////////////////////////////////////////////////////////////*/

  ///@notice exclusive user - when != address(0x0), other users are removed from whitelist
  ///        intended for short term use during incidence response, escrow, migration
  address public exclusiveUser;

  ///@notice guard authorization
  mapping(address => bool) public isGuard;

  ///@notice keeper authorization
  mapping(address => bool) public isKeeper;

  ///@notice user authorization, can use deposit, withdraw
  mapping(address => bool) public isUser;

  ///@notice swapper authorization, can use swap
  mapping(address => bool) public isSwapper;

  ///@notice incentive manager authorization, can set incentives for swaps on Fyde
  mapping(address => bool) public isIncentiveManager;

  /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

  event ExclusiveUserSet(address indexed);
  event GuardAdded(address indexed);
  event GuardRemoved(address indexed);
  event KeeperAdded(address indexed);
  event KeeperRemoved(address indexed);
  event UserAdded(address indexed);
  event UserRemoved(address indexed);
  event IncentiveManagerAdded(address indexed);
  event IncentiveManagerRemoved(address indexed);
  event SwapperAdded(address indexed);
  event SwapperRemoved(address indexed);

  /*//////////////////////////////////////////////////////////////
                               SETTER
    //////////////////////////////////////////////////////////////*/

  function setExclusiveUser(address _exclusiveUser) external onlyOwner {
    exclusiveUser = _exclusiveUser;
    emit ExclusiveUserSet(_exclusiveUser);
  }

  function addGuard(address _guard) external onlyOwner {
    isGuard[_guard] = true;
    emit GuardAdded(_guard);
  }

  function removeGuard(address _guard) external onlyOwner {
    isGuard[_guard] = false;
    emit GuardRemoved(_guard);
  }

  function addKeeper(address _keeper) external onlyOwner {
    isKeeper[_keeper] = true;
    emit KeeperAdded(_keeper);
  }

  function removeKeeper(address _keeper) external onlyOwner {
    isKeeper[_keeper] = false;
    emit KeeperRemoved(_keeper);
  }

  function addUser(address[] calldata _user) external onlyOwner {
    for (uint256 i; i < _user.length; ++i) {
      isUser[_user[i]] = true;
      emit UserAdded(_user[i]);
    }
  }

  function removeUser(address[] calldata _user) external onlyOwner {
    for (uint256 i; i < _user.length; ++i) {
      isUser[_user[i]] = false;
      emit UserRemoved(_user[i]);
    }
  }

  function addIncentiveManager(address _incentiveManager) external onlyOwner {
    isIncentiveManager[_incentiveManager] = true;
    emit IncentiveManagerAdded(_incentiveManager);
  }

  function removeIncentiveManager(address _incentiveManager) external onlyOwner {
    isIncentiveManager[_incentiveManager] = false;
    emit IncentiveManagerRemoved(_incentiveManager);
  }

  function addSwapper(address _swapper) external onlyOwner {
    isSwapper[_swapper] = true;
    emit SwapperAdded(_swapper);
  }

  function removeSwapper(address _swapper) external onlyOwner {
    isSwapper[_swapper] = false;
    emit SwapperRemoved(_swapper);
  }

  /*//////////////////////////////////////////////////////////////
                               MODIFIER
    //////////////////////////////////////////////////////////////*/

  ///@notice only a registered keeper can access
  modifier onlyKeeper() {
    if (!isKeeper[msg.sender]) revert Unauthorized();
    _;
  }

  ///@notice only a registered guard can access
  modifier onlyGuard() {
    if (!isGuard[msg.sender]) revert Unauthorized();
    _;
  }

  ///@dev whitelisting address(0x0) disables whitelist -> full permissionless access
  ///@dev setting exclusiveUser to != address(0x0) blocks everyone else
  ///     - intended for escrow, incidence response and migration
  modifier onlyUser() {
    // if whitelist is not disabeld and user not whitelisted -> no access
    if (!isUser[address(0x0)] && !isUser[msg.sender]) revert Unauthorized();
    // if exclusive user exists and is not user -> no accesss
    if (exclusiveUser != address(0x0) && exclusiveUser != msg.sender) revert Unauthorized();
    _;
  }

  ///@dev whitelisting address(0x0) disables whitelist -> full permissionless access
  ///@dev setting exclusiveUser to != address(0x0) blocks everyone else
  ///     - intended for escrow, incidence response and migration
  modifier onlySwapper() {
    if (!isSwapper[address(0x0)] && !isSwapper[msg.sender]) revert Unauthorized();
    if (exclusiveUser != address(0x0) && exclusiveUser != msg.sender) revert Unauthorized();
    _;
  }
}

File 22 of 41 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 23 of 41 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 24 of 41 : IUniswapV3PoolImmutables.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that never changes
/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
interface IUniswapV3PoolImmutables {
    /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
    /// @return The contract address
    function factory() external view returns (address);

    /// @notice The first of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token0() external view returns (address);

    /// @notice The second of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token1() external view returns (address);

    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
    /// @return The fee
    function fee() external view returns (uint24);

    /// @notice The pool tick spacing
    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
    /// This value is an int24 to avoid casting even though it is always positive.
    /// @return The tick spacing
    function tickSpacing() external view returns (int24);

    /// @notice The maximum amount of position liquidity that can use any tick in the range
    /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
    /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
    /// @return The max amount of liquidity per tick
    function maxLiquidityPerTick() external view returns (uint128);
}

File 25 of 41 : MathUtil.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

pragma solidity 0.8.19;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUtil {
  /**
   * @dev Returns the largest of two numbers.
   */
  function max(int256 a, int256 b) internal pure returns (int256) {
    return a >= b ? a : b;
  }

  /**
   * @dev Returns the smallest of two numbers.
   */
  function min(int256 a, int256 b) internal pure returns (int256) {
    return a < b ? a : b;
  }

  /**
   * @dev Returns the smallest of two numbers.
   */
  function min(uint256 a, uint256 b) internal pure returns (uint256) {
    return a < b ? a : b;
  }
}

File 26 of 41 : SafeCast.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * Utilities that convert numeric types avoiding silent overflows.
 */
import "./SafeCast/SafeCastU32.sol";
import "./SafeCast/SafeCastI32.sol";
import "./SafeCast/SafeCastI24.sol";
import "./SafeCast/SafeCastU56.sol";
import "./SafeCast/SafeCastI56.sol";
import "./SafeCast/SafeCastU64.sol";
import "./SafeCast/SafeCastI128.sol";
import "./SafeCast/SafeCastI256.sol";
import "./SafeCast/SafeCastU128.sol";
import "./SafeCast/SafeCastU160.sol";
import "./SafeCast/SafeCastU256.sol";
import "./SafeCast/SafeCastAddress.sol";
import "./SafeCast/SafeCastBytes32.sol";

File 27 of 41 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 28 of 41 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 29 of 41 : SafeCastU32.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU32 {
    error OverflowUint32ToInt32();

    function toInt(uint32 x) internal pure returns (int32) {
        // -------------------------------o=========>----------------------
        // ----------------------<========o========>x----------------------
        if (x > uint32(type(int32).max)) {
            revert OverflowUint32ToInt32();
        }

        return int32(x);
    }

    function to256(uint32 x) internal pure returns (uint256) {
        return uint256(x);
    }

    function to56(uint32 x) internal pure returns (uint56) {
        return uint56(x);
    }
}

File 30 of 41 : SafeCastI32.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastI32 {
    error OverflowInt32ToUint32();

    function toUint(int32 x) internal pure returns (uint32) {
        // ----------------------<========o========>----------------------
        // ----------------------xxxxxxxxxo=========>----------------------
        if (x < 0) {
            revert OverflowInt32ToUint32();
        }

        return uint32(x);
    }
}

File 31 of 41 : SafeCastI24.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastI24 {
    function to256(int24 x) internal pure returns (int256) {
        return int256(x);
    }
}

File 32 of 41 : SafeCastU56.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU56 {
    error OverflowUint56ToInt56();

    function toInt(uint56 x) internal pure returns (int56) {
        // -------------------------------o=========>----------------------
        // ----------------------<========o========>x----------------------
        if (x > uint56(type(int56).max)) {
            revert OverflowUint56ToInt56();
        }

        return int56(x);
    }
}

File 33 of 41 : SafeCastI56.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastI56 {
    error OverflowInt56ToInt24();

    function to24(int56 x) internal pure returns (int24) {
        // ----------------------<========o========>-----------------------
        // ----------------------xxx<=====o=====>xxx-----------------------
        if (x < int(type(int24).min) || x > int(type(int24).max)) {
            revert OverflowInt56ToInt24();
        }

        return int24(x);
    }
}

File 34 of 41 : SafeCastU64.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU64 {
    error OverflowUint64ToInt64();

    function toInt(uint64 x) internal pure returns (int64) {
        // -------------------------------o=========>----------------------
        // ----------------------<========o========>x----------------------
        if (x > uint64(type(int64).max)) {
            revert OverflowUint64ToInt64();
        }

        return int64(x);
    }
}

File 35 of 41 : SafeCastI128.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastI128 {
    error OverflowInt128ToUint128();
    error OverflowInt128ToInt32();

    function toUint(int128 x) internal pure returns (uint128) {
        // ----------------<==============o==============>-----------------
        // ----------------xxxxxxxxxxxxxxxo===============>----------------
        if (x < 0) {
            revert OverflowInt128ToUint128();
        }

        return uint128(x);
    }

    function to256(int128 x) internal pure returns (int256) {
        return int256(x);
    }

    function to32(int128 x) internal pure returns (int32) {
        // ----------------<==============o==============>-----------------
        // ----------------xxxxxxxxxxxx<==o==>xxxxxxxxxxxx-----------------
        if (x < int(type(int32).min) || x > int(type(int32).max)) {
            revert OverflowInt128ToInt32();
        }

        return int32(x);
    }

    function zero() internal pure returns (int128) {
        return int128(0);
    }
}

File 36 of 41 : SafeCastI256.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastI256 {
    error OverflowInt256ToUint256();
    error OverflowInt256ToInt128();
    error OverflowInt256ToInt24();

    function to128(int256 x) internal pure returns (int128) {
        // ----<==========================o===========================>----
        // ----xxxxxxxxxxxx<==============o==============>xxxxxxxxxxxxx----
        if (x < int256(type(int128).min) || x > int256(type(int128).max)) {
            revert OverflowInt256ToInt128();
        }

        return int128(x);
    }

    function to24(int256 x) internal pure returns (int24) {
        // ----<==========================o===========================>----
        // ----xxxxxxxxxxxxxxxxxxxx<======o=======>xxxxxxxxxxxxxxxxxxxx----
        if (x < int256(type(int24).min) || x > int256(type(int24).max)) {
            revert OverflowInt256ToInt24();
        }

        return int24(x);
    }

    function toUint(int256 x) internal pure returns (uint256) {
        // ----<==========================o===========================>----
        // ----xxxxxxxxxxxxxxxxxxxxxxxxxxxo===============================>
        if (x < 0) {
            revert OverflowInt256ToUint256();
        }

        return uint256(x);
    }

    function zero() internal pure returns (int256) {
        return int256(0);
    }
}

File 37 of 41 : SafeCastU128.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU128 {
    error OverflowUint128ToInt128();

    function to256(uint128 x) internal pure returns (uint256) {
        return uint256(x);
    }

    function toInt(uint128 x) internal pure returns (int128) {
        // -------------------------------o===============>----------------
        // ----------------<==============o==============>x----------------
        if (x > uint128(type(int128).max)) {
            revert OverflowUint128ToInt128();
        }

        return int128(x);
    }

    function toBytes32(uint128 x) internal pure returns (bytes32) {
        return bytes32(uint256(x));
    }
}

File 38 of 41 : SafeCastU160.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU160 {
    function to256(uint160 x) internal pure returns (uint256) {
        return uint256(x);
    }
}

File 39 of 41 : SafeCastU256.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU256 {
    error OverflowUint256ToUint128();
    error OverflowUint256ToInt256();
    error OverflowUint256ToUint64();
    error OverflowUint256ToUint32();
    error OverflowUint256ToUint160();

    function to128(uint256 x) internal pure returns (uint128) {
        // -------------------------------o===============================>
        // -------------------------------o===============>xxxxxxxxxxxxxxxx
        if (x > type(uint128).max) {
            revert OverflowUint256ToUint128();
        }

        return uint128(x);
    }

    function to64(uint256 x) internal pure returns (uint64) {
        // -------------------------------o===============================>
        // -------------------------------o======>xxxxxxxxxxxxxxxxxxxxxxxxx
        if (x > type(uint64).max) {
            revert OverflowUint256ToUint64();
        }

        return uint64(x);
    }

    function to32(uint256 x) internal pure returns (uint32) {
        // -------------------------------o===============================>
        // -------------------------------o===>xxxxxxxxxxxxxxxxxxxxxxxxxxxx
        if (x > type(uint32).max) {
            revert OverflowUint256ToUint32();
        }

        return uint32(x);
    }

    function to160(uint256 x) internal pure returns (uint160) {
        // -------------------------------o===============================>
        // -------------------------------o==================>xxxxxxxxxxxxx
        if (x > type(uint160).max) {
            revert OverflowUint256ToUint160();
        }

        return uint160(x);
    }

    function toBytes32(uint256 x) internal pure returns (bytes32) {
        return bytes32(x);
    }

    function toInt(uint256 x) internal pure returns (int256) {
        // -------------------------------o===============================>
        // ----<==========================o===========================>xxxx
        if (x > uint256(type(int256).max)) {
            revert OverflowUint256ToInt256();
        }

        return int256(x);
    }
}

File 40 of 41 : SafeCastAddress.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastAddress {
    function toBytes32(address x) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(x)));
    }
}

File 41 of 41 : SafeCastBytes32.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastBytes32 {
    function toAddress(bytes32 x) internal pure returns (address) {
        return address(uint160(uint256(x)));
    }

    function toUint(bytes32 x) internal pure returns (uint) {
        return uint(x);
    }
}

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": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_oracleModule","type":"address"},{"internalType":"address","name":"_govModule","type":"address"},{"internalType":"uint8","name":"_deviationThreshold","type":"uint8"},{"internalType":"bytes32[]","name":"_relayerActions","type":"bytes32[]"},{"internalType":"uint256[]","name":"_gasUsages","type":"uint256[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ActionPaused","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"AssetIsNotQuarantined","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"AssetIsQuarantined","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"AssetNotAllowedInGovernancePool","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"AssetNotSupported","type":"error"},{"inputs":[],"name":"AssetPriceNotAvailable","type":"error"},{"inputs":[],"name":"DuplicatesAssets","type":"error"},{"inputs":[],"name":"EmptyQueue","type":"error"},{"inputs":[],"name":"InconsistentLengths","type":"error"},{"inputs":[],"name":"IncorrectNumOfAsset","type":"error"},{"inputs":[],"name":"NoRequest","type":"error"},{"inputs":[{"internalType":"uint256","name":"received","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"NotEnoughGas","type":"error"},{"inputs":[],"name":"NotEnoughRequests","type":"error"},{"inputs":[{"internalType":"uint128","name":"duration","type":"uint128"}],"name":"ShortQurantineDuration","type":"error"},{"inputs":[{"internalType":"uint128","name":"currentExpiration","type":"uint128"},{"internalType":"uint128","name":"expiration","type":"uint128"}],"name":"ShortenedExpiration","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"SlippageExceed","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"SwapDisabled","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ValueOutOfBounds","type":"error"},{"inputs":[],"name":"ZeroParameter","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint128","name":"expirationTime","type":"uint128"}],"name":"AddedToQuarantine","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"requestId","type":"uint32"},{"components":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address[]","name":"assetIn","type":"address[]"},{"internalType":"uint256[]","name":"amountIn","type":"uint256[]"},{"internalType":"address[]","name":"assetOut","type":"address[]"},{"internalType":"uint256[]","name":"amountOut","type":"uint256[]"},{"internalType":"bool","name":"keepGovRights","type":"bool"},{"internalType":"uint256","name":"slippageChecker","type":"uint256"}],"indexed":false,"internalType":"struct RequestData","name":"request","type":"tuple"}],"name":"DepositRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"ExclusiveUserSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"GuardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"GuardRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"IncentiveManagerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"IncentiveManagerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"KeeperAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"KeeperRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipTransferCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"requestId","type":"uint32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"ProcessRequestFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"requestId","type":"uint32"}],"name":"ProcessRequestSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"RemovedFromQuarantine","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"requestId","type":"uint32"},{"components":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address[]","name":"assetIn","type":"address[]"},{"internalType":"uint256[]","name":"amountIn","type":"uint256[]"},{"internalType":"address[]","name":"assetOut","type":"address[]"},{"internalType":"uint256[]","name":"amountOut","type":"uint256[]"},{"internalType":"bool","name":"keepGovRights","type":"bool"},{"internalType":"uint256","name":"slippageChecker","type":"uint256"}],"indexed":false,"internalType":"struct RequestData","name":"request","type":"tuple"}],"name":"SwapRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"SwapperAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"SwapperRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"UserAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"}],"name":"UserRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"requestId","type":"uint32"},{"components":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address[]","name":"assetIn","type":"address[]"},{"internalType":"uint256[]","name":"amountIn","type":"uint256[]"},{"internalType":"address[]","name":"assetOut","type":"address[]"},{"internalType":"uint256[]","name":"amountOut","type":"uint256[]"},{"internalType":"bool","name":"keepGovRights","type":"bool"},{"internalType":"uint256","name":"slippageChecker","type":"uint256"}],"indexed":false,"internalType":"struct RequestData","name":"request","type":"tuple"}],"name":"WithdrawRequested","type":"event"},{"inputs":[],"name":"GOVERNANCE_MODULE","outputs":[{"internalType":"contract IGovernanceModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ASSET_TO_REQUEST","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BATCH_SIZE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"actionToGasUsage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_guard","type":"address"}],"name":"addGuard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_incentiveManager","type":"address"}],"name":"addIncentiveManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keeper","type":"address"}],"name":"addKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"}],"name":"addSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"uint128","name":"_duration","type":"uint128"}],"name":"addToQuarantine","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_user","type":"address[]"}],"name":"addUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelTransferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checker_processRequest","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checker_updateProtocolAUM","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_recipient","type":"address"}],"name":"collectEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_numRequests","type":"uint64"}],"name":"deleteRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deviationThreshold","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exclusiveUser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fyde","outputs":[{"internalType":"contract IFyde","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"action","type":"string"},{"internalType":"bool","name":"isGovDeposit","type":"bool"}],"name":"getGasToForward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumPendingRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"idx","type":"uint64"}],"name":"getRequest","outputs":[{"components":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"requestor","type":"address"},{"internalType":"address[]","name":"assetIn","type":"address[]"},{"internalType":"uint256[]","name":"amountIn","type":"uint256[]"},{"internalType":"address[]","name":"assetOut","type":"address[]"},{"internalType":"uint256[]","name":"amountOut","type":"uint256[]"},{"internalType":"bool","name":"keepGovRights","type":"bool"},{"internalType":"uint256","name":"slippageChecker","type":"uint256"}],"internalType":"struct RequestData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_assets","type":"address[]"}],"name":"isAnyQuarantined","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isGuard","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isIncentiveManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isKeeper","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"isQuarantined","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isSwapper","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isUser","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"min_quarantine_duration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleModule","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseProtocol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_protocolAUM","type":"uint256"}],"name":"processRequests","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"quarantineList","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"removeFromQuarantine","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_guard","type":"address"}],"name":"removeGuard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_incentiveManager","type":"address"}],"name":"removeIncentiveManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keeper","type":"address"}],"name":"removeKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"}],"name":"removeSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_user","type":"address[]"}],"name":"removeUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct UserRequest[]","name":"_userRequest","type":"tuple[]"},{"internalType":"bool","name":"_keepGovRights","type":"bool"},{"internalType":"uint256","name":"_minTRSYExpected","type":"uint256"}],"name":"requestDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct UserRequest","name":"_userRequest","type":"tuple"},{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_maxTRSYToPay","type":"uint256"}],"name":"requestGovernanceWithdraw","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_assetIn","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_assetOut","type":"address"},{"internalType":"uint256","name":"_minAmountOut","type":"uint256"}],"name":"requestSwap","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct UserRequest[]","name":"_userRequest","type":"tuple[]"},{"internalType":"uint256","name":"_maxTRSYToPay","type":"uint256"}],"name":"requestWithdraw","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_exclusiveUser","type":"address"}],"name":"setExclusiveUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fyde","type":"address"}],"name":"setFyde","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oracleModule","type":"address"}],"name":"setOracleModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_min_quarantine_duration","type":"uint256"}],"name":"set_min_quarantine_duration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseProtocol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_threshold","type":"uint16"}],"name":"updateDeviationThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_relayerAction","type":"bytes32"},{"internalType":"uint256","name":"_gasUsage","type":"uint256"}],"name":"updateGasUsageToAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nAum","type":"uint256"}],"name":"updateProtocolAUM","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405262015180600a553480156200001857600080fd5b50604051620052f2380380620052f28339810160408190526200003b91620002b4565b600280546001600160a01b0319163390811790915560405181906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350600d80546001600160a01b0319166001600160a01b03878116919091179091558416608052620000b160ff841662000130565b60005b82518110156200012457818181518110620000d357620000d3620003bb565b6020026020010151600e6000858481518110620000f457620000f4620003bb565b602002602001015181526020019081526020016000208190555080806200011b90620003d1565b915050620000b4565b505050505050620003f9565b6002546001600160a01b031633146200015b576040516282b42960e81b815260040160405180910390fd5b600a8161ffff1610806200017457506103e88161ffff16115b156200019357604051636d4d4a6760e11b815260040160405180910390fd5b600d805461ffff909216600160c01b0261ffff60c01b19909216919091179055565b80516001600160a01b0381168114620001cd57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715620002135762000213620001d2565b604052919050565b60006001600160401b03821115620002375762000237620001d2565b5060051b60200190565b600082601f8301126200025357600080fd5b815160206200026c62000266836200021b565b620001e8565b82815260059290921b840181019181810190868411156200028c57600080fd5b8286015b84811015620002a9578051835291830191830162000290565b509695505050505050565b600080600080600060a08688031215620002cd57600080fd5b620002d886620001b5565b94506020620002e9818801620001b5565b9450604087015160ff811681146200030057600080fd5b60608801519094506001600160401b03808211156200031e57600080fd5b818901915089601f8301126200033357600080fd5b81516200034462000266826200021b565b81815260059190911b8301840190848101908c8311156200036457600080fd5b938501935b82851015620003845784518252938501939085019062000369565b60808c015190975094505050808311156200039e57600080fd5b5050620003ae8882890162000241565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b600060018201620003f257634e487b7160e01b600052601160045260246000fd5b5060010190565b608051614ec86200042a6000396000818161043001528181612bd6015281816133b901526142f20152614ec86000f3fe6080604052600436106103975760003560e01c806399385006116101dc578063cdfca7f211610102578063e30c3978116100a0578063f2fde38b1161006f578063f2fde38b14610b4e578063f6164a3714610b6e578063f80c6d9c14610b8e578063f93b6be514610bae57600080fd5b8063e30c397814610ad9578063e787caa414610af9578063ec2a099214610b19578063ed6b5ad514610b3957600080fd5b8063d94ad837116100dc578063d94ad83714610a4f578063d9fd6fb814610a84578063dbf6248914610aa4578063ddb015c814610ab957600080fd5b8063cdfca7f214610a04578063cfdbf25414610a24578063d307983d14610a3957600080fd5b8063b6ea98011161017a578063c87eed4611610149578063c87eed4614610984578063c9d6293c146109a4578063cbbf67ec146109b7578063cc88688b146109e457600080fd5b8063b6ea980114610911578063b813a8df14610924578063bae2be5614610944578063c073fd741461096457600080fd5b8063b19e52dc116101b6578063b19e52dc1461087e578063b588c10b14610891578063b6235016146108c1578063b64230ba146108e157600080fd5b80639938500614610810578063a01605c314610830578063affed0e01461084557600080fd5b8063526631fb116102c1578063769315a41161025f57806392fede001161022e57806392fede001461079457806393263b8a146107a9578063979edeba146107c957806398fbfbea146107f057600080fd5b8063769315a41461071f57806379ba50971461073f5780637cb1d3df146107545780638da5cb5b1461077457600080fd5b80636913a63c1161029b5780636913a63c1461069a5780636ba42aaa146106ba5780636d9b83ec146106ea578063708968c11461070a57600080fd5b8063526631fb146106435780635c975abb14610666578063608530051461068757600080fd5b80633ddc66ba11610339578063485d6ef811610308578063485d6ef81461058e578063489c1202146105d357806349bcb7b2146106035780634fd8cb941461062357600080fd5b80633ddc66ba146104fe5780633f2d41311461051e5780634032b72b1461053e5780634209fff11461055e57600080fd5b80631a366bb7116103755780631a366bb71461041e57806320157d0a1461046a578063245a4e721461047f57806339663ef2146104b057600080fd5b806305e2f3801461039c57806314ae9f2e146103dc57806315db9304146103fe575b600080fd5b3480156103a857600080fd5b506103c96103b7366004614516565b600e6020526000908152604090205481565b6040519081526020015b60405180910390f35b3480156103e857600080fd5b506103fc6103f7366004614544565b610bc3565b005b34801561040a57600080fd5b506103fc610419366004614561565b610c36565b34801561042a57600080fd5b506104527f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016103d3565b34801561047657600080fd5b506103fc610c72565b34801561048b57600080fd5b50600d546104a090600160d81b900460ff1681565b60405190151581526020016103d3565b3480156104bc57600080fd5b506104e66104cb366004614544565b600b602052600090815260409020546001600160801b031681565b6040516001600160801b0390911681526020016103d3565b34801561050a57600080fd5b506103fc610519366004614516565b610ce4565b34801561052a57600080fd5b506103fc610539366004614544565b6111eb565b34801561054a57600080fd5b506103fc610559366004614544565b61125e565b34801561056a57600080fd5b506104a0610579366004614544565b60076020526000908152604090205460ff1681565b34801561059a57600080fd5b506104a06105a9366004614544565b6001600160a01b03166000908152600b60205260409020546001600160801b034281169116101590565b3480156105df57600080fd5b506104a06105ee366004614544565b60056020526000908152604090205460ff1681565b34801561060f57600080fd5b506103fc61061e366004614583565b6112d4565b34801561062f57600080fd5b50600c54610452906001600160a01b031681565b34801561064f57600080fd5b5061065861130f565b6040516103d39291906145fc565b34801561067257600080fd5b50600d546104a090600160d01b900460ff1681565b6103fc61069536600461466a565b61141c565b3480156106a657600080fd5b506103fc6106b5366004614544565b6118d6565b3480156106c657600080fd5b506104a06106d5366004614544565b60066020526000908152604090205460ff1681565b3480156106f657600080fd5b506103fc610705366004614516565b61194c565b34801561071657600080fd5b506103c961197b565b34801561072b57600080fd5b506103fc61073a3660046146b5565b6119aa565b34801561074b57600080fd5b506103fc611aa5565b34801561076057600080fd5b506103fc61076f366004614729565b611b2c565b34801561078057600080fd5b50600254610452906001600160a01b031681565b3480156107a057600080fd5b506103fc611bae565b3480156107b557600080fd5b50600454610452906001600160a01b031681565b3480156107d557600080fd5b506107de600581565b60405160ff90911681526020016103d3565b3480156107fc57600080fd5b506103fc61080b366004614544565b611c22565b34801561081c57600080fd5b506103fc61082b366004614544565b611c98565b34801561083c57600080fd5b50610658611d0e565b34801561085157600080fd5b50600d5461086990600160a01b900463ffffffff1681565b60405163ffffffff90911681526020016103d3565b6103fc61088c366004614793565b611eed565b34801561089d57600080fd5b506104a06108ac366004614544565b60096020526000908152604090205460ff1681565b3480156108cd57600080fd5b506103fc6108dc366004614544565b612273565b3480156108ed57600080fd5b506104a06108fc366004614544565b60086020526000908152604090205460ff1681565b6103fc61091f366004614815565b6122e6565b34801561093057600080fd5b506103fc61093f36600461485d565b612781565b34801561095057600080fd5b506103fc61095f366004614544565b6128c1565b34801561097057600080fd5b506103fc61097f366004614544565b61299d565b34801561099057600080fd5b506103fc61099f366004614544565b6129e9565b6103fc6109b23660046148b7565b612a5c565b3480156109c357600080fd5b506109d76109d2366004614583565b613067565b6040516103d39190614a40565b3480156109f057600080fd5b50600d54610452906001600160a01b031681565b348015610a1057600080fd5b506103fc610a1f3660046146b5565b61325f565b348015610a3057600080fd5b506107de600381565b348015610a4557600080fd5b506103c9600a5481565b348015610a5b57600080fd5b50600d54610a7190600160c01b900461ffff1681565b60405161ffff90911681526020016103d3565b348015610a9057600080fd5b506103c9610a9f366004614a53565b613355565b348015610ab057600080fd5b506103fc613545565b348015610ac557600080fd5b506103fc610ad4366004614544565b6135b9565b348015610ae557600080fd5b50600354610452906001600160a01b031681565b348015610b0557600080fd5b506103fc610b14366004614516565b61362d565b348015610b2557600080fd5b506103fc610b34366004614544565b6136bd565b348015610b4557600080fd5b506103fc613709565b348015610b5a57600080fd5b506103fc610b69366004614544565b61377d565b348015610b7a57600080fd5b506103fc610b89366004614544565b6137f3565b348015610b9a57600080fd5b50610452610ba9366004614af8565b61387d565b348015610bba57600080fd5b506103fc613909565b6002546001600160a01b03163314610bed576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260066020526040808220805460ff19169055517fa7a775c2c8141f7985c111748ec31c11e5e44b83528e105c8d1d4e8e6b81cf809190a250565b6002546001600160a01b03163314610c60576040516282b42960e81b815260040160405180910390fd5b6000918252600e602052604090912055565b3360009081526005602052604090205460ff16610ca1576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d81b191690556040514281527faaa520fdd7d2c83061d632fa017b0432407e798818af63ea908589fceda39ab7906020015b60405180910390a1565b3360009081526006602052604090205460ff16610d13576040516282b42960e81b815260040160405180910390fd5b600d54600160d01b900460ff1615610d3e576040516318c0a57b60e31b815260040160405180910390fd5b6000610d4861197b565b905080600003610d6b576040516333ff64eb60e21b815260040160405180910390fd5b6003811115610d7b576003610d7d565b805b9050600c60009054906101000a90046001600160a01b03166001600160a01b0316639c016ffd6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610dcf57600080fd5b505af1158015610de3573d6000803e3d6000fd5b5084925060009150505b828110156111e5576000610e016000613067565b6040015151118015610e1f5750610e186000613067565b6080015151155b15610f61576000610e2e613975565b600c54604051632d58fd7560e01b81529192506001600160a01b031690632d58fd7590610e619086908590600401614ba9565b6020604051808303816000875af1925050508015610e9c575060408051601f3d908101601f19168201909252610e9991810190614bc2565b60015b610f10573d808015610eca576040519150601f19603f3d011682016040523d82523d6000602084013e610ecf565b606091505b5081516040517fce8eca4722db427aba6fc7b0ac201e3ef02e2860d150008647ae8c726bf6456e91610f02918490614bdb565b60405180910390a150610f5b565b610f1a8185614c10565b825160405163ffffffff90911681529094507f502fce9d813ab813dd143cef3aaa92752fc1768a61d064bd9819b3b5b1f470ae9060200160405180910390a1505b506111d3565b6000610f6d6000613067565b6080015151118015610f8b5750610f846000613067565b6040015151155b15611040576000610f9a613975565b600c54604051633ea1a23f60e11b81529192506001600160a01b031690637d43447e90610fcd9086908590600401614ba9565b6020604051808303816000875af1925050508015611008575060408051601f3d908101601f1916820190925261100591810190614bc2565b60015b611036573d808015610eca576040519150601f19603f3d011682016040523d82523d6000602084013e610ecf565b610f1a8185614c23565b61104a6000613067565b6040015151600114801561106c57506110636000613067565b60800151516001145b156111d357600061107b613975565b600c54604051633d655c3760e11b81529192506001600160a01b031690637acab86e906110ae9088908590600401614ba9565b6020604051808303816000875af19250505080156110e9575060408051601f3d908101601f191682019092526110e691810190614bc2565b60015b61115d573d808015611117576040519150601f19603f3d011682016040523d82523d6000602084013e61111c565b606091505b5081516040517fce8eca4722db427aba6fc7b0ac201e3ef02e2860d150008647ae8c726bf6456e9161114f918490614bdb565b60405180910390a1506111d1565b6000811315611177576111708185614c10565b9350611193565b60008112156111935761118981614c36565b6111709085614c23565b815160405163ffffffff90911681527f502fce9d813ab813dd143cef3aaa92752fc1768a61d064bd9819b3b5b1f470ae9060200160405180910390a1505b505b806111dd81614c52565b915050610ded565b50505050565b6002546001600160a01b03163314611215576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260086020526040808220805460ff19169055517f1bdacea439f3d52c1495fc881858088a33f2ad0cc4a6a1a08780ca96eb6b702d9190a250565b6002546001600160a01b03163314611288576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260066020526040808220805460ff19166001179055517f1584773458d98c71b34a270ee1100b3a42889bf91e3b7a858563b684c24d838e9190a250565b3360009081526005602052604090205460ff16611303576040516282b42960e81b815260040160405180910390fd5b61130c81613c4e565b50565b6000606061131b61197b565b600003611352575050604080518082019091526012815271139bc81c195b991a5b99c81c995c5d595cdd60721b6020820152600091565b600c54604080516383307f4360e01b815290516000926001600160a01b0316916383307f439160048083019260209291908290030181865afa15801561139c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c09190614bc2565b90506001306001600160a01b0316633ddc66ba836040516024016113e691815260200190565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505092509250509091565b600d54600160d01b900460ff1615611447576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff1615801561149257503360009081526007602052604090205460ff16155b156114af576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b0316158015906114d457506004546001600160a01b03163314155b156114f1576040516282b42960e81b815260040160405180910390fd5b60006114fc83613d60565b60405160200161150c9190614c6b565b60408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa15801561156c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115909190614bc2565b6000838152600e60205260409020546115a99190614ca5565b9050803410156115da57604051634fd394c160e11b8152346004820152602481018290526044015b60405180910390fd5b6115e384613e8b565b6000846001600160401b038111156115fd576115fd61474d565b604051908082528060200260200182016040528015611626578160200160208202803683370190505b5090506000856001600160401b038111156116435761164361474d565b60405190808252806020026020018201604052801561166c578160200160208202803683370190505b50905060005b868110156117735761169e88888381811061168f5761168f614cbc565b90506040020160200135613eb6565b6116ce8888838181106116b3576116b3614cbc565b6116c99260206040909202019081019150614544565b613ed7565b8787828181106116e0576116e0614cbc565b6116f69260206040909202019081019150614544565b83828151811061170857611708614cbc565b60200260200101906001600160a01b031690816001600160a01b03168152505087878281811061173a5761173a614cbc565b9050604002016020013582828151811061175657611756614cbc565b60209081029190910101528061176b81614c52565b915050611672565b5061177d82613efe565b6117878282613fbc565b61179082613fde565b6117998261401e565b6040805161010081018252600d54600160a01b900463ffffffff1681523360208201526000918101826040519080825280602002602001820160405280156117eb578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561181c578160200160208202803683370190505b5081526020810185905260408101849052600060608201526080018790529050611845816140c5565b7fa75ba6b66423ec9aec6231c21821a3ecfda0db0813ccf69d66808e73b8c860e8600d60149054906101000a900463ffffffff1682604051611888929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff169060146118ad83614cf1565b91906101000a81548163ffffffff021916908363ffffffff160217905550505050505050505050565b6002546001600160a01b03163314611900576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260056020526040808220805460ff19166001179055517f93405f05cd04f0d1bd875f2de00f1f3890484ffd0589248953bdfd29ba7f2f599190a250565b6002546001600160a01b03163314611976576040516282b42960e81b815260040160405180910390fd5b600a55565b6000805461199c906001600160401b0380821691600160401b900416614d14565b6001600160401b0316905090565b6002546001600160a01b031633146119d4576040516282b42960e81b815260040160405180910390fd5b60005b81811015611aa0576000600760008585858181106119f7576119f7614cbc565b9050602002016020810190611a0c9190614544565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055828282818110611a4657611a46614cbc565b9050602002016020810190611a5b9190614544565b6001600160a01b03167fe9dce8c992623ce791725b21e857e33248d1f190a25b5168313420eebdaae99d60405160405180910390a2611a9981614c52565b90506119d7565b505050565b6003546001600160a01b03163314611acf576040516282b42960e81b815260040160405180910390fd5b60028054600380546001600160a01b03198084166001600160a01b038381169182179096559116909155604051929091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6002546001600160a01b03163314611b56576040516282b42960e81b815260040160405180910390fd5b600a8161ffff161080611b6e57506103e88161ffff16115b15611b8c57604051636d4d4a6760e11b815260040160405180910390fd5b600d805461ffff909216600160c01b0261ffff60c01b19909216919091179055565b6002546001600160a01b03163314611bd8576040516282b42960e81b815260040160405180910390fd5b6003546040516001600160a01b03909116907f6ecd4842251bedd053b09547c0fabaab9ec98506ebf24469e8dd5560412ed37f90600090a2600380546001600160a01b0319169055565b6002546001600160a01b03163314611c4c576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19166001179055517ff7de8a78655219727a7c0d0db42a97c024f91cb131c5713063c555ce7d7c56529190a250565b6002546001600160a01b03163314611cc2576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260086020526040808220805460ff19166001179055517f459edbd4f641bfd6cb8eba5ac1b9e7a4629853efd00cc505d055fd3b051867e09190a250565b600060606000600c60009054906101000a90046001600160a01b03166001600160a01b0316638be859ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d8b9190614bc2565b90506000600c60009054906101000a90046001600160a01b03166001600160a01b03166383307f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611de2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e069190614bc2565b905081158015611e1557508015155b15611e75576001306001600160a01b031663e787caa483604051602401611e3e91815260200190565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050509350935050509091565b600d54611e909083908390600160c01b900461ffff166141e9565b15611ec95760006040518060400160405280600f81526020016e41554d20697320696e2072616e676560881b8152509350935050509091565b6001306001600160a01b031663e787caa483604051602401611e3e91815260200190565b600d54600160d01b900460ff1615611f18576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff16158015611f6357503360009081526007602052604090205460ff16155b15611f80576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b031615801590611fa557506004546001600160a01b03163314155b15611fc2576040516282b42960e81b815260040160405180910390fd5b6040517457697468647261775f476f7665726e616e63655f3160581b602082015260009060350160408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa158015612049573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206d9190614bc2565b6000838152600e60205260409020546120869190614ca5565b9050803410156120b257604051634fd394c160e11b8152346004820152602481018290526044016115d1565b336001600160a01b038516148015906120d657506002546001600160a01b03163314155b156120f3576040516282b42960e81b815260040160405180910390fd5b6040805160018082528183019092526000916020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090506121458760200151613eb6565b865161215090613ed7565b86600001518260008151811061216857612168614cbc565b60200260200101906001600160a01b031690816001600160a01b0316815250508660200151816000815181106121a0576121a0614cbc565b6020026020010181815250506121b582613fde565b6121be8261401e565b6040805161010081018252600d54600160a01b900463ffffffff1681526001600160a01b0388166020820152600091810182604051908082528060200260200182016040528015612219578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561224a578160200160208202803683370190505b5081526020810185905260408101849052600160608201526080018790529050611845816140c5565b6002546001600160a01b0316331461229d576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260056020526040808220805460ff19169055517f59926e0a78d12238b668b31c8e3f6ece235a59a00ede111d883e255b68c4d0489190a250565b600d54600160d81b900460ff1615612311576040516318c0a57b60e31b815260040160405180910390fd5b6000805260086020527f5eff886ea0ce6ca488a3d6e336d6c0f75f46d19b42c06ce5ee98e42c96d256c75460ff1615801561235c57503360009081526008602052604090205460ff16155b15612379576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b03161580159061239e57506004546001600160a01b03163314155b156123bb576040516282b42960e81b815260040160405180910390fd5b604051630537761760e41b602082015260009060240160408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa158015612431573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124559190614bc2565b6000838152600e602052604090205461246e9190614ca5565b90508034101561249a57604051634fd394c160e11b8152346004820152602481018290526044016115d1565b6124a385613eb6565b6124ac86613ed7565b6124b584613ed7565b836001600160a01b0316866001600160a01b0316036124e757604051630df7483960e41b815260040160405180910390fd5b604080516002808252606082018352600092602083019080368337019050509050868160008151811061251c5761251c614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050848160018151811061255057612550614cbc565b60200260200101906001600160a01b031690816001600160a01b0316815250506125798161401e565b61258281613fde565b61258b81614231565b604080516001808252818301909252600091602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337505060408051600180825281830190925292935060009291506020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090508a8460008151811061262a5761262a614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050898360008151811061265e5761265e614cbc565b602002602001018181525050888260008151811061267e5761267e614cbc565b6001600160a01b039092166020928302919091018201526040805161010081018252600d5463ffffffff600160a01b90910416815233928101929092528101859052606081018490526080810183905260a08101829052600060c082015260e081018990526126ec816140c5565b7f806ee47a6104a6e7ede1e15a9c9f384c0706b79fcea8a9edb136fe3b51b496b6600d60149054906101000a900463ffffffff168260405161272f929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff1690601461275483614cf1565b91906101000a81548163ffffffff021916908363ffffffff16021790555050505050505050505050505050565b3360009081526005602052604090205460ff166127b0576040516282b42960e81b815260040160405180910390fd5b600a54816001600160801b031610156127e757604051632797027760e01b81526001600160801b03821660048201526024016115d1565b60006127f38242614d3b565b6001600160a01b0384166000908152600b60205260409020549091506001600160801b03908116908216811061284f57604051630a4dc5d360e31b81526001600160801b038083166004830152831660248201526044016115d1565b6001600160a01b0384166000818152600b602090815260409182902080546001600160801b0319166001600160801b0387169081179091558251938452908301527f6a8b3a105d0f4bca0c6d25f6f59282c2cac3e2acdd3f3466dabe9725f3299703910160405180910390a150505050565b3360009081526005602052604090205460ff166128f0576040516282b42960e81b815260040160405180910390fd5b6001600160a01b0381166000908152600b60205260409020546001600160801b034281169116101561294057604051637407be0b60e11b81526001600160a01b03821660048201526024016115d1565b6001600160a01b0381166000818152600b602090815260409182902080546001600160801b031916905590519182527f975fea2c466fdf1d3e8dd9969713fbfde0822e5b3921c18c0003e033f216bf5a910160405180910390a150565b6002546001600160a01b031633146129c7576040516282b42960e81b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b6002546001600160a01b03163314612a13576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19169055517f4d6789b83afc897fab8472d33d3f7cc1fba254215a1cd007ce51f3c5bf6f57729190a250565b600d54600160d01b900460ff1615612a87576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff16158015612ad257503360009081526007602052604090205460ff16155b15612aef576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b031615801590612b1457506004546001600160a01b03163314155b15612b31576040516282b42960e81b815260040160405180910390fd5b600082612b6c57612b4184613d60565b604051602001612b519190614d5b565b60405160208183030381529060405280519060200120612b9c565b612b7584613d60565b604051602001612b859190614d94565b604051602081830303815290604052805190602001205b6000818152600e60205260409020549091508315612cd657604051630311ead160e61b815233600482015260009081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063c47ab44090602401602060405180830381865afa158015612c1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c419190614dcf565b6001600160a01b031614612c56576001612c59565b60005b905080612cd057600e6000604051602001612c88906d283937bc3cafa1b932b0ba34b7b760911b8152600e0190565b60405160208183030381529060405280519060200120815260200190815260200160002054600e600085815260200190815260200160002054612ccb9190614c10565b612cd2565b815b9150505b600d60009054906101000a90046001600160a01b03166001600160a01b031663e3a031bf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d4d9190614bc2565b612d579082614ca5565b905080341015612d8357604051634fd394c160e11b8152346004820152602481018290526044016115d1565b612d8c85613e8b565b6000856001600160401b03811115612da657612da661474d565b604051908082528060200260200182016040528015612dcf578160200160208202803683370190505b5090506000866001600160401b03811115612dec57612dec61474d565b604051908082528060200260200182016040528015612e15578160200160208202803683370190505b50905060005b87811015612ef257612e3889898381811061168f5761168f614cbc565b612e4d8989838181106116b3576116b3614cbc565b888882818110612e5f57612e5f614cbc565b612e759260206040909202019081019150614544565b838281518110612e8757612e87614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050888882818110612eb957612eb9614cbc565b90506040020160200135828281518110612ed557612ed5614cbc565b602090810291909101015280612eea81614c52565b915050612e1b565b50612efd8282613fbc565b612f0682613efe565b612f0f8261401e565b612f1882613fde565b8515612f2757612f27826142d8565b6040805161010081018252600d54600160a01b900463ffffffff168152336020820152908101839052606081018290526000906080810182604051908082528060200260200182016040528015612f88578160200160208202803683370190505b5081526020016000604051908082528060200260200182016040528015612fb9578160200160208202803683370190505b50815288151560208201526040018790529050612fd5816140c5565b7f8f1d75282d5f6e805e4525ffa180022a0be9390ae16b9391e8bae3b0f85e39bf600d60149054906101000a900463ffffffff1682604051613018929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff1690601461303d83614cf1565b91906101000a81548163ffffffff021916908363ffffffff16021790555050505050505050505050565b61306f6143ed565b600080546001919061308b9085906001600160401b0316614dec565b6001600160401b031681526020808201929092526040908101600020815161010081018352815463ffffffff8116825264010000000090046001600160a01b03168185015260018201805484518187028101870186528181529295939486019383018282801561312457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613106575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561317c57602002820191906000526020600020905b815481526020019060010190808311613168575b50505050508152602001600382018054806020026020016040519081016040528092919081815260200182805480156131de57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116131c0575b505050505081526020016004820180548060200260200160405190810160405280929190818152602001828054801561323657602002820191906000526020600020905b815481526020019060010190808311613222575b5050509183525050600582015460ff161515602082015260069091015460409091015292915050565b6002546001600160a01b03163314613289576040516282b42960e81b815260040160405180910390fd5b60005b81811015611aa0576001600760008585858181106132ac576132ac614cbc565b90506020020160208101906132c19190614544565b6001600160a01b031681526020810191909152604001600020805460ff19169115159190911790558282828181106132fb576132fb614cbc565b90506020020160208101906133109190614544565b6001600160a01b03167f19ef9a4877199f89440a26acb26895ec02ed86f2df1aeaa90dc18041b892f71f60405160405180910390a261334e81614c52565b905061328c565b600080836040516020016133699190614e0c565b60408051601f1981840301815291815281516020928301206000818152600e90935291205490915083156134b957604051630311ead160e61b815233600482015260009081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063c47ab44090602401602060405180830381865afa158015613400573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134249190614dcf565b6001600160a01b03161461343957600161343c565b60005b9050806134b357600e600060405160200161346b906d283937bc3cafa1b932b0ba34b7b760911b8152600e0190565b60405160208183030381529060405280519060200120815260200190815260200160002054600e6000858152602001908152602001600020546134ae9190614c10565b6134b5565b815b9150505b600d60009054906101000a90046001600160a01b03166001600160a01b031663e3a031bf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561350c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135309190614bc2565b61353a9082614ca5565b925050505b92915050565b3360009081526005602052604090205460ff16613574576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d01b1916600160d01b1790556040517f68b095021b1f40fe513109f513c66692f0b3219aee674a69f4efc57badb8201d90610cda9042815260200190565b6002546001600160a01b031633146135e3576040516282b42960e81b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0383169081179091556040517fc720a84179457777bd6ab883473f5ae8b0d37c9d100fd66f070ddcbd35af020f90600090a250565b3360009081526006602052604090205460ff1661365c576040516282b42960e81b815260040160405180910390fd5b600c546040516339e1f2a960e21b8152600481018390526001600160a01b039091169063e787caa490602401600060405180830381600087803b1580156136a257600080fd5b505af11580156136b6573d6000803e3d6000fd5b5050505050565b6002546001600160a01b031633146136e7576040516282b42960e81b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b3360009081526005602052604090205460ff16613738576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d81b1916600160d81b1790556040517f68b095021b1f40fe513109f513c66692f0b3219aee674a69f4efc57badb8201d90610cda9042815260200190565b6002546001600160a01b031633146137a7576040516282b42960e81b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b03831690811790915560405133907f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270090600090a350565b6002546001600160a01b0316331461381d576040516282b42960e81b815260040160405180910390fd5b60405147906000906001600160a01b0384169083908381818185875af1925050503d806000811461386a576040519150601f19603f3d011682016040523d82523d6000602084013e61386f565b606091505b5050905080611aa057600080fd5b6000805b8251811015613900576138d183828151811061389f5761389f614cbc565b60200260200101516001600160a01b03166000908152600b60205260409020546001600160801b034281169116101590565b156138f8578281815181106138e8576138e8614cbc565b6020026020010151915050919050565b600101613881565b50600092915050565b3360009081526005602052604090205460ff16613938576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d01b191690556040514281527faaa520fdd7d2c83061d632fa017b0432407e798818af63ea908589fceda39ab790602001610cda565b61397d6143ed565b600080546001600160401b0316818061399583614e28565b91906101000a8154816001600160401b0302191690836001600160401b0316021790555090506000800160089054906101000a90046001600160401b03166001600160401b0316816001600160401b031603613a04576040516331dcf2b560e01b815260040160405180910390fd5b6001600160401b0381166000908152600160208181526040808420815161010081018352815463ffffffff8116825264010000000090046001600160a01b03168185015293810180548351818602810186018552818152929493860193830182828015613a9a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613a7c575b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015613af257602002820191906000526020600020905b815481526020019060010190808311613ade575b5050505050815260200160038201805480602002602001604051908101604052809291908181526020018280548015613b5457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613b36575b5050505050815260200160048201805480602002602001604051908101604052809291908181526020018280548015613bac57602002820191906000526020600020905b815481526020019060010190808311613b98575b5050509183525050600582015460ff1615156020808301919091526006909201546040918201526001600160401b038516600090815260019283905290812080546001600160c01b031916815592935090613c0990830182614443565b613c17600283016000614443565b613c25600383016000614443565b613c33600483016000614443565b5060058101805460ff19169055600060069091015592915050565b613c5661197b565b816001600160401b03161115613c7f5760405163ad2ce56160e01b815260040160405180910390fd5b600080546001600160401b031690613c978383614dec565b90505b806001600160401b0316826001600160401b03161015613d3b576001600160401b0382166000908152600160208190526040822080546001600160c01b03191681559190613cea90830182614443565b613cf8600283016000614443565b613d06600383016000614443565b613d14600483016000614443565b5060058101805460ff19169055600060069091015581613d3381614e28565b925050613c9a565b506000805467ffffffffffffffff19166001600160401b039290921691909117905550565b606081600003613d875750506040805180820190915260018152600360fc1b602082015290565b8160005b8115613db15780613d9b81614c52565b9150613daa9050600a83614e44565b9150613d8b565b6000816001600160401b03811115613dcb57613dcb61474d565b6040519080825280601f01601f191660200182016040528015613df5576020820181803683370190505b509050815b8515613e8257613e0b600182614c23565b90506000613e1a600a88614e44565b613e2590600a614ca5565b613e2f9088614c23565b613e3a906030614e66565b905060008160f81b905080848481518110613e5757613e57614cbc565b60200101906001600160f81b031916908160001a905350613e79600a89614e44565b97505050613dfa565b50949350505050565b6005811180613e98575080155b1561130c57604051631d7317ad60e31b815260040160405180910390fd5b8060000361130c5760405163339d6ef160e21b815260040160405180910390fd5b6001600160a01b03811661130c5760405163339d6ef160e21b815260040160405180910390fd5b60005b60018251613f0f9190614c23565b811015613fb8576000613f23826001614c10565b90505b8251811015613fa557828181518110613f4157613f41614cbc565b60200260200101516001600160a01b0316838381518110613f6457613f64614cbc565b60200260200101516001600160a01b031603613f9357604051630df7483960e41b815260040160405180910390fd5b80613f9d81614c52565b915050613f26565b5080613fb081614c52565b915050613f01565b5050565b8051825114613fb85760405163b1f40f7760e01b815260040160405180910390fd5b6000613fe98261387d565b90506001600160a01b03811615613fb85760405163fc31f2eb60e01b81526001600160a01b03821660048201526024016115d1565b600c5460405163db10e60f60e01b81526000916001600160a01b03169063db10e60f9061404f908590600401614e7f565b602060405180830381865afa15801561406c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140909190614dcf565b90506001600160a01b03811615613fb857604051632777a68f60e11b81526001600160a01b03821660048201526024016115d1565b600080548291600191600160401b90046001600160401b03168160086140ea83614e28565b82546101009290920a6001600160401b038181021990931691831602179091551681526020808201929092526040908101600020835181548585015163ffffffff9092166001600160c01b0319909116176401000000006001600160a01b0390921691909102178155908301518051919261416d92600185019290910190614461565b50606082015180516141899160028401916020909101906144c6565b50608082015180516141a5916003840191602090910190614461565b5060a082015180516141c19160048401916020909101906144c6565b5060c082015160058201805460ff191691151591909117905560e09091015160069091015550565b6000806141f6858461439d565b9050600061420486856143ce565b90508185108061421357508085115b156142235760009250505061422a565b6001925050505b9392505050565b600c5460405163aeabb17f60e01b81526000916001600160a01b03169063aeabb17f90614262908590600401614e7f565b602060405180830381865afa15801561427f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142a39190614dcf565b90506001600160a01b03811615613fb8576040516303e4acb760e01b81526001600160a01b03821660048201526024016115d1565b60405163eb2fea2d60e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063eb2fea2d90614327908590600401614e7f565b602060405180830381865afa158015614344573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143689190614dcf565b90506001600160a01b03811615613fb85760405163b3ef117360e01b81526001600160a01b03821660048201526024016115d1565b612710818103908211611388198290048411820217156143bc57600080fd5b61271092026113880191909104919050565b612710810161271019821161138819829004841117156143bc57600080fd5b604051806101000160405280600063ffffffff16815260200160006001600160a01b0316815260200160608152602001606081526020016060815260200160608152602001600015158152602001600081525090565b508054600082559060005260206000209081019061130c9190614501565b8280548282559060005260206000209081019282156144b6579160200282015b828111156144b657825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614481565b506144c2929150614501565b5090565b8280548282559060005260206000209081019282156144b6579160200282015b828111156144b65782518255916020019190600101906144e6565b5b808211156144c25760008155600101614502565b60006020828403121561452857600080fd5b5035919050565b6001600160a01b038116811461130c57600080fd5b60006020828403121561455657600080fd5b813561422a8161452f565b6000806040838503121561457457600080fd5b50508035926020909101359150565b60006020828403121561459557600080fd5b81356001600160401b038116811461422a57600080fd5b60005b838110156145c75781810151838201526020016145af565b50506000910152565b600081518084526145e88160208601602086016145ac565b601f01601f19169290920160200192915050565b821515815260406020820152600061461760408301846145d0565b949350505050565b60008083601f84011261463157600080fd5b5081356001600160401b0381111561464857600080fd5b6020830191508360208260061b850101111561466357600080fd5b9250929050565b60008060006040848603121561467f57600080fd5b83356001600160401b0381111561469557600080fd5b6146a18682870161461f565b909790965060209590950135949350505050565b600080602083850312156146c857600080fd5b82356001600160401b03808211156146df57600080fd5b818501915085601f8301126146f357600080fd5b81358181111561470257600080fd5b8660208260051b850101111561471757600080fd5b60209290920196919550909350505050565b60006020828403121561473b57600080fd5b813561ffff8116811461422a57600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561478b5761478b61474d565b604052919050565b600080600083850360808112156147a957600080fd5b60408112156147b757600080fd5b50604051604081018181106001600160401b03821117156147da576147da61474d565b60405284356147e88161452f565b815260208581013590820152925060408401356148048161452f565b929592945050506060919091013590565b6000806000806080858703121561482b57600080fd5b84356148368161452f565b935060208501359250604085013561484d8161452f565b9396929550929360600135925050565b6000806040838503121561487057600080fd5b823561487b8161452f565b915060208301356001600160801b038116811461489757600080fd5b809150509250929050565b803580151581146148b257600080fd5b919050565b600080600080606085870312156148cd57600080fd5b84356001600160401b038111156148e357600080fd5b6148ef8782880161461f565b90955093506149029050602086016148a2565b9396929550929360400135925050565b600081518084526020808501945080840160005b8381101561494b5781516001600160a01b031687529582019590820190600101614926565b509495945050505050565b600081518084526020808501945080840160005b8381101561494b5781518752958201959082019060010161496a565b805163ffffffff168252600061010060208301516149af60208601826001600160a01b03169052565b5060408301518160408601526149c782860182614912565b915050606083015184820360608601526149e18282614956565b915050608083015184820360808601526149fb8282614912565b91505060a083015184820360a0860152614a158282614956565b91505060c0830151614a2b60c086018215159052565b5060e083015160e08501528091505092915050565b60208152600061422a6020830184614986565b60008060408385031215614a6657600080fd5b82356001600160401b0380821115614a7d57600080fd5b818501915085601f830112614a9157600080fd5b8135602082821115614aa557614aa561474d565b614ab7601f8301601f19168201614763565b92508183528781838601011115614acd57600080fd5b81818501828501376000818385010152829550614aeb8188016148a2565b9450505050509250929050565b60006020808385031215614b0b57600080fd5b82356001600160401b0380821115614b2257600080fd5b818501915085601f830112614b3657600080fd5b813581811115614b4857614b4861474d565b8060051b9150614b59848301614763565b8181529183018401918481019088841115614b7357600080fd5b938501935b83851015614b9d5784359250614b8d8361452f565b8282529385019390850190614b78565b98975050505050505050565b8281526040602082015260006146176040830184614986565b600060208284031215614bd457600080fd5b5051919050565b63ffffffff8316815260406020820152600061461760408301846145d0565b634e487b7160e01b600052601160045260246000fd5b8082018082111561353f5761353f614bfa565b8181038181111561353f5761353f614bfa565b6000600160ff1b8201614c4b57614c4b614bfa565b5060000390565b600060018201614c6457614c64614bfa565b5060010190565b7157697468647261775f5374616e646172645f60701b815260008251614c988160128501602087016145ac565b9190910160120192915050565b808202811582820484141761353f5761353f614bfa565b634e487b7160e01b600052603260045260246000fd5b63ffffffff831681526040602082015260006146176040830184614986565b600063ffffffff808316818103614d0a57614d0a614bfa565b6001019392505050565b6001600160401b03828116828216039080821115614d3457614d34614bfa565b5092915050565b6001600160801b03818116838216019080821115614d3457614d34614bfa565b704465706f7369745f5374616e646172645f60781b815260008251614d878160118501602087016145ac565b9190910160110192915050565b724465706f7369745f476f7665726e616e63655f60681b815260008251614dc28160138501602087016145ac565b9190910160130192915050565b600060208284031215614de157600080fd5b815161422a8161452f565b6001600160401b03818116838216019080821115614d3457614d34614bfa565b60008251614e1e8184602087016145ac565b9190910192915050565b60006001600160401b03808316818103614d0a57614d0a614bfa565b600082614e6157634e487b7160e01b600052601260045260246000fd5b500490565b60ff818116838216019081111561353f5761353f614bfa565b60208152600061422a602083018461491256fea2646970667358221220d0814db886cb03bb1e8f13667a8fb195ba1e886c5131e80b719c8d76e36df0b764736f6c634300081300330000000000000000000000009b122361e8708be33b785e44fce4d6ca86ab6c5a000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000012b79b4593a13085d73229968d1b867c62a32331077e69c330a19117713527cd3693f2818dadb98b16d09e2e242225f069ec196a1ccaac86c5e21a6a7dc211e7a64975a0313faffb91d1d3bdb7733fce82fc79c31dbce3725a7bd30a4c0309dd5430f5e54773ee1f907ea369134d807f2f5e9a077a596ae33f202ee9efd8a79041f06650d97d5fa110714d9ba428a531bcce2994ecf368233773b269c33c5284c0a5d57abcb8c5cffcc475879522d4079eb000aab40af8c8dfa2c6825a4ec7fa8e75a5f64a69880afad6dc81260417ab82e188266bb12f38be7aa677f6951f46a66284fc93b15bdbaa3497df91f72b049f9391b0a5cdb0c70261858cd800e78ef8295e35ff4b9162c303c6b01738b2b55433e7738994defbd6b415a61d44ab34adccaa06416913f4262df5915ac4295960f5c2ee4a97453e8ecd22441a0a7ed1721330c7e431ba9511c00ecdb3bd76484b80368e8cdb3ad45b7a38620e42645b4a1139c37a0006eb1553293885ecb8074975c26e075b1415e0bb8e4d1a6ff6879bbfef723e9aab4abce7940c28392120fc89b0eb7dfefd757fd1437a57bd866b1bb04d9a2639a5b4cf65e1bb56c25943b4c18a01953da0b432c33c871422bd44b00373e344542a62092609e9d9f47788dd56d305552e13b39ee9458cdd3ca862d5cd646213e4389ec59b9bb8d0b5ea7574d143ad62171d149c5ac0da897d7efef56109d294b2402434fc7c2c34271ecc470039cb01a6946c8658989975ef72be6dbce316f0d9d2a3affa97de1d99bb2aac0538e2666d0d8545545ead241ef0ccab00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c00000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c00000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000061a80

Deployed Bytecode

0x6080604052600436106103975760003560e01c806399385006116101dc578063cdfca7f211610102578063e30c3978116100a0578063f2fde38b1161006f578063f2fde38b14610b4e578063f6164a3714610b6e578063f80c6d9c14610b8e578063f93b6be514610bae57600080fd5b8063e30c397814610ad9578063e787caa414610af9578063ec2a099214610b19578063ed6b5ad514610b3957600080fd5b8063d94ad837116100dc578063d94ad83714610a4f578063d9fd6fb814610a84578063dbf6248914610aa4578063ddb015c814610ab957600080fd5b8063cdfca7f214610a04578063cfdbf25414610a24578063d307983d14610a3957600080fd5b8063b6ea98011161017a578063c87eed4611610149578063c87eed4614610984578063c9d6293c146109a4578063cbbf67ec146109b7578063cc88688b146109e457600080fd5b8063b6ea980114610911578063b813a8df14610924578063bae2be5614610944578063c073fd741461096457600080fd5b8063b19e52dc116101b6578063b19e52dc1461087e578063b588c10b14610891578063b6235016146108c1578063b64230ba146108e157600080fd5b80639938500614610810578063a01605c314610830578063affed0e01461084557600080fd5b8063526631fb116102c1578063769315a41161025f57806392fede001161022e57806392fede001461079457806393263b8a146107a9578063979edeba146107c957806398fbfbea146107f057600080fd5b8063769315a41461071f57806379ba50971461073f5780637cb1d3df146107545780638da5cb5b1461077457600080fd5b80636913a63c1161029b5780636913a63c1461069a5780636ba42aaa146106ba5780636d9b83ec146106ea578063708968c11461070a57600080fd5b8063526631fb146106435780635c975abb14610666578063608530051461068757600080fd5b80633ddc66ba11610339578063485d6ef811610308578063485d6ef81461058e578063489c1202146105d357806349bcb7b2146106035780634fd8cb941461062357600080fd5b80633ddc66ba146104fe5780633f2d41311461051e5780634032b72b1461053e5780634209fff11461055e57600080fd5b80631a366bb7116103755780631a366bb71461041e57806320157d0a1461046a578063245a4e721461047f57806339663ef2146104b057600080fd5b806305e2f3801461039c57806314ae9f2e146103dc57806315db9304146103fe575b600080fd5b3480156103a857600080fd5b506103c96103b7366004614516565b600e6020526000908152604090205481565b6040519081526020015b60405180910390f35b3480156103e857600080fd5b506103fc6103f7366004614544565b610bc3565b005b34801561040a57600080fd5b506103fc610419366004614561565b610c36565b34801561042a57600080fd5b506104527f000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e181565b6040516001600160a01b0390911681526020016103d3565b34801561047657600080fd5b506103fc610c72565b34801561048b57600080fd5b50600d546104a090600160d81b900460ff1681565b60405190151581526020016103d3565b3480156104bc57600080fd5b506104e66104cb366004614544565b600b602052600090815260409020546001600160801b031681565b6040516001600160801b0390911681526020016103d3565b34801561050a57600080fd5b506103fc610519366004614516565b610ce4565b34801561052a57600080fd5b506103fc610539366004614544565b6111eb565b34801561054a57600080fd5b506103fc610559366004614544565b61125e565b34801561056a57600080fd5b506104a0610579366004614544565b60076020526000908152604090205460ff1681565b34801561059a57600080fd5b506104a06105a9366004614544565b6001600160a01b03166000908152600b60205260409020546001600160801b034281169116101590565b3480156105df57600080fd5b506104a06105ee366004614544565b60056020526000908152604090205460ff1681565b34801561060f57600080fd5b506103fc61061e366004614583565b6112d4565b34801561062f57600080fd5b50600c54610452906001600160a01b031681565b34801561064f57600080fd5b5061065861130f565b6040516103d39291906145fc565b34801561067257600080fd5b50600d546104a090600160d01b900460ff1681565b6103fc61069536600461466a565b61141c565b3480156106a657600080fd5b506103fc6106b5366004614544565b6118d6565b3480156106c657600080fd5b506104a06106d5366004614544565b60066020526000908152604090205460ff1681565b3480156106f657600080fd5b506103fc610705366004614516565b61194c565b34801561071657600080fd5b506103c961197b565b34801561072b57600080fd5b506103fc61073a3660046146b5565b6119aa565b34801561074b57600080fd5b506103fc611aa5565b34801561076057600080fd5b506103fc61076f366004614729565b611b2c565b34801561078057600080fd5b50600254610452906001600160a01b031681565b3480156107a057600080fd5b506103fc611bae565b3480156107b557600080fd5b50600454610452906001600160a01b031681565b3480156107d557600080fd5b506107de600581565b60405160ff90911681526020016103d3565b3480156107fc57600080fd5b506103fc61080b366004614544565b611c22565b34801561081c57600080fd5b506103fc61082b366004614544565b611c98565b34801561083c57600080fd5b50610658611d0e565b34801561085157600080fd5b50600d5461086990600160a01b900463ffffffff1681565b60405163ffffffff90911681526020016103d3565b6103fc61088c366004614793565b611eed565b34801561089d57600080fd5b506104a06108ac366004614544565b60096020526000908152604090205460ff1681565b3480156108cd57600080fd5b506103fc6108dc366004614544565b612273565b3480156108ed57600080fd5b506104a06108fc366004614544565b60086020526000908152604090205460ff1681565b6103fc61091f366004614815565b6122e6565b34801561093057600080fd5b506103fc61093f36600461485d565b612781565b34801561095057600080fd5b506103fc61095f366004614544565b6128c1565b34801561097057600080fd5b506103fc61097f366004614544565b61299d565b34801561099057600080fd5b506103fc61099f366004614544565b6129e9565b6103fc6109b23660046148b7565b612a5c565b3480156109c357600080fd5b506109d76109d2366004614583565b613067565b6040516103d39190614a40565b3480156109f057600080fd5b50600d54610452906001600160a01b031681565b348015610a1057600080fd5b506103fc610a1f3660046146b5565b61325f565b348015610a3057600080fd5b506107de600381565b348015610a4557600080fd5b506103c9600a5481565b348015610a5b57600080fd5b50600d54610a7190600160c01b900461ffff1681565b60405161ffff90911681526020016103d3565b348015610a9057600080fd5b506103c9610a9f366004614a53565b613355565b348015610ab057600080fd5b506103fc613545565b348015610ac557600080fd5b506103fc610ad4366004614544565b6135b9565b348015610ae557600080fd5b50600354610452906001600160a01b031681565b348015610b0557600080fd5b506103fc610b14366004614516565b61362d565b348015610b2557600080fd5b506103fc610b34366004614544565b6136bd565b348015610b4557600080fd5b506103fc613709565b348015610b5a57600080fd5b506103fc610b69366004614544565b61377d565b348015610b7a57600080fd5b506103fc610b89366004614544565b6137f3565b348015610b9a57600080fd5b50610452610ba9366004614af8565b61387d565b348015610bba57600080fd5b506103fc613909565b6002546001600160a01b03163314610bed576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260066020526040808220805460ff19169055517fa7a775c2c8141f7985c111748ec31c11e5e44b83528e105c8d1d4e8e6b81cf809190a250565b6002546001600160a01b03163314610c60576040516282b42960e81b815260040160405180910390fd5b6000918252600e602052604090912055565b3360009081526005602052604090205460ff16610ca1576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d81b191690556040514281527faaa520fdd7d2c83061d632fa017b0432407e798818af63ea908589fceda39ab7906020015b60405180910390a1565b3360009081526006602052604090205460ff16610d13576040516282b42960e81b815260040160405180910390fd5b600d54600160d01b900460ff1615610d3e576040516318c0a57b60e31b815260040160405180910390fd5b6000610d4861197b565b905080600003610d6b576040516333ff64eb60e21b815260040160405180910390fd5b6003811115610d7b576003610d7d565b805b9050600c60009054906101000a90046001600160a01b03166001600160a01b0316639c016ffd6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610dcf57600080fd5b505af1158015610de3573d6000803e3d6000fd5b5084925060009150505b828110156111e5576000610e016000613067565b6040015151118015610e1f5750610e186000613067565b6080015151155b15610f61576000610e2e613975565b600c54604051632d58fd7560e01b81529192506001600160a01b031690632d58fd7590610e619086908590600401614ba9565b6020604051808303816000875af1925050508015610e9c575060408051601f3d908101601f19168201909252610e9991810190614bc2565b60015b610f10573d808015610eca576040519150601f19603f3d011682016040523d82523d6000602084013e610ecf565b606091505b5081516040517fce8eca4722db427aba6fc7b0ac201e3ef02e2860d150008647ae8c726bf6456e91610f02918490614bdb565b60405180910390a150610f5b565b610f1a8185614c10565b825160405163ffffffff90911681529094507f502fce9d813ab813dd143cef3aaa92752fc1768a61d064bd9819b3b5b1f470ae9060200160405180910390a1505b506111d3565b6000610f6d6000613067565b6080015151118015610f8b5750610f846000613067565b6040015151155b15611040576000610f9a613975565b600c54604051633ea1a23f60e11b81529192506001600160a01b031690637d43447e90610fcd9086908590600401614ba9565b6020604051808303816000875af1925050508015611008575060408051601f3d908101601f1916820190925261100591810190614bc2565b60015b611036573d808015610eca576040519150601f19603f3d011682016040523d82523d6000602084013e610ecf565b610f1a8185614c23565b61104a6000613067565b6040015151600114801561106c57506110636000613067565b60800151516001145b156111d357600061107b613975565b600c54604051633d655c3760e11b81529192506001600160a01b031690637acab86e906110ae9088908590600401614ba9565b6020604051808303816000875af19250505080156110e9575060408051601f3d908101601f191682019092526110e691810190614bc2565b60015b61115d573d808015611117576040519150601f19603f3d011682016040523d82523d6000602084013e61111c565b606091505b5081516040517fce8eca4722db427aba6fc7b0ac201e3ef02e2860d150008647ae8c726bf6456e9161114f918490614bdb565b60405180910390a1506111d1565b6000811315611177576111708185614c10565b9350611193565b60008112156111935761118981614c36565b6111709085614c23565b815160405163ffffffff90911681527f502fce9d813ab813dd143cef3aaa92752fc1768a61d064bd9819b3b5b1f470ae9060200160405180910390a1505b505b806111dd81614c52565b915050610ded565b50505050565b6002546001600160a01b03163314611215576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260086020526040808220805460ff19169055517f1bdacea439f3d52c1495fc881858088a33f2ad0cc4a6a1a08780ca96eb6b702d9190a250565b6002546001600160a01b03163314611288576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260066020526040808220805460ff19166001179055517f1584773458d98c71b34a270ee1100b3a42889bf91e3b7a858563b684c24d838e9190a250565b3360009081526005602052604090205460ff16611303576040516282b42960e81b815260040160405180910390fd5b61130c81613c4e565b50565b6000606061131b61197b565b600003611352575050604080518082019091526012815271139bc81c195b991a5b99c81c995c5d595cdd60721b6020820152600091565b600c54604080516383307f4360e01b815290516000926001600160a01b0316916383307f439160048083019260209291908290030181865afa15801561139c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c09190614bc2565b90506001306001600160a01b0316633ddc66ba836040516024016113e691815260200190565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505092509250509091565b600d54600160d01b900460ff1615611447576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff1615801561149257503360009081526007602052604090205460ff16155b156114af576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b0316158015906114d457506004546001600160a01b03163314155b156114f1576040516282b42960e81b815260040160405180910390fd5b60006114fc83613d60565b60405160200161150c9190614c6b565b60408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa15801561156c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115909190614bc2565b6000838152600e60205260409020546115a99190614ca5565b9050803410156115da57604051634fd394c160e11b8152346004820152602481018290526044015b60405180910390fd5b6115e384613e8b565b6000846001600160401b038111156115fd576115fd61474d565b604051908082528060200260200182016040528015611626578160200160208202803683370190505b5090506000856001600160401b038111156116435761164361474d565b60405190808252806020026020018201604052801561166c578160200160208202803683370190505b50905060005b868110156117735761169e88888381811061168f5761168f614cbc565b90506040020160200135613eb6565b6116ce8888838181106116b3576116b3614cbc565b6116c99260206040909202019081019150614544565b613ed7565b8787828181106116e0576116e0614cbc565b6116f69260206040909202019081019150614544565b83828151811061170857611708614cbc565b60200260200101906001600160a01b031690816001600160a01b03168152505087878281811061173a5761173a614cbc565b9050604002016020013582828151811061175657611756614cbc565b60209081029190910101528061176b81614c52565b915050611672565b5061177d82613efe565b6117878282613fbc565b61179082613fde565b6117998261401e565b6040805161010081018252600d54600160a01b900463ffffffff1681523360208201526000918101826040519080825280602002602001820160405280156117eb578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561181c578160200160208202803683370190505b5081526020810185905260408101849052600060608201526080018790529050611845816140c5565b7fa75ba6b66423ec9aec6231c21821a3ecfda0db0813ccf69d66808e73b8c860e8600d60149054906101000a900463ffffffff1682604051611888929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff169060146118ad83614cf1565b91906101000a81548163ffffffff021916908363ffffffff160217905550505050505050505050565b6002546001600160a01b03163314611900576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260056020526040808220805460ff19166001179055517f93405f05cd04f0d1bd875f2de00f1f3890484ffd0589248953bdfd29ba7f2f599190a250565b6002546001600160a01b03163314611976576040516282b42960e81b815260040160405180910390fd5b600a55565b6000805461199c906001600160401b0380821691600160401b900416614d14565b6001600160401b0316905090565b6002546001600160a01b031633146119d4576040516282b42960e81b815260040160405180910390fd5b60005b81811015611aa0576000600760008585858181106119f7576119f7614cbc565b9050602002016020810190611a0c9190614544565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055828282818110611a4657611a46614cbc565b9050602002016020810190611a5b9190614544565b6001600160a01b03167fe9dce8c992623ce791725b21e857e33248d1f190a25b5168313420eebdaae99d60405160405180910390a2611a9981614c52565b90506119d7565b505050565b6003546001600160a01b03163314611acf576040516282b42960e81b815260040160405180910390fd5b60028054600380546001600160a01b03198084166001600160a01b038381169182179096559116909155604051929091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6002546001600160a01b03163314611b56576040516282b42960e81b815260040160405180910390fd5b600a8161ffff161080611b6e57506103e88161ffff16115b15611b8c57604051636d4d4a6760e11b815260040160405180910390fd5b600d805461ffff909216600160c01b0261ffff60c01b19909216919091179055565b6002546001600160a01b03163314611bd8576040516282b42960e81b815260040160405180910390fd5b6003546040516001600160a01b03909116907f6ecd4842251bedd053b09547c0fabaab9ec98506ebf24469e8dd5560412ed37f90600090a2600380546001600160a01b0319169055565b6002546001600160a01b03163314611c4c576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19166001179055517ff7de8a78655219727a7c0d0db42a97c024f91cb131c5713063c555ce7d7c56529190a250565b6002546001600160a01b03163314611cc2576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260086020526040808220805460ff19166001179055517f459edbd4f641bfd6cb8eba5ac1b9e7a4629853efd00cc505d055fd3b051867e09190a250565b600060606000600c60009054906101000a90046001600160a01b03166001600160a01b0316638be859ec6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d8b9190614bc2565b90506000600c60009054906101000a90046001600160a01b03166001600160a01b03166383307f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611de2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e069190614bc2565b905081158015611e1557508015155b15611e75576001306001600160a01b031663e787caa483604051602401611e3e91815260200190565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050509350935050509091565b600d54611e909083908390600160c01b900461ffff166141e9565b15611ec95760006040518060400160405280600f81526020016e41554d20697320696e2072616e676560881b8152509350935050509091565b6001306001600160a01b031663e787caa483604051602401611e3e91815260200190565b600d54600160d01b900460ff1615611f18576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff16158015611f6357503360009081526007602052604090205460ff16155b15611f80576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b031615801590611fa557506004546001600160a01b03163314155b15611fc2576040516282b42960e81b815260040160405180910390fd5b6040517457697468647261775f476f7665726e616e63655f3160581b602082015260009060350160408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa158015612049573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206d9190614bc2565b6000838152600e60205260409020546120869190614ca5565b9050803410156120b257604051634fd394c160e11b8152346004820152602481018290526044016115d1565b336001600160a01b038516148015906120d657506002546001600160a01b03163314155b156120f3576040516282b42960e81b815260040160405180910390fd5b6040805160018082528183019092526000916020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090506121458760200151613eb6565b865161215090613ed7565b86600001518260008151811061216857612168614cbc565b60200260200101906001600160a01b031690816001600160a01b0316815250508660200151816000815181106121a0576121a0614cbc565b6020026020010181815250506121b582613fde565b6121be8261401e565b6040805161010081018252600d54600160a01b900463ffffffff1681526001600160a01b0388166020820152600091810182604051908082528060200260200182016040528015612219578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561224a578160200160208202803683370190505b5081526020810185905260408101849052600160608201526080018790529050611845816140c5565b6002546001600160a01b0316331461229d576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260056020526040808220805460ff19169055517f59926e0a78d12238b668b31c8e3f6ece235a59a00ede111d883e255b68c4d0489190a250565b600d54600160d81b900460ff1615612311576040516318c0a57b60e31b815260040160405180910390fd5b6000805260086020527f5eff886ea0ce6ca488a3d6e336d6c0f75f46d19b42c06ce5ee98e42c96d256c75460ff1615801561235c57503360009081526008602052604090205460ff16155b15612379576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b03161580159061239e57506004546001600160a01b03163314155b156123bb576040516282b42960e81b815260040160405180910390fd5b604051630537761760e41b602082015260009060240160408051601f198184030181528282528051602091820120600d5463e3a031bf60e01b855292519094506000936001600160a01b039093169263e3a031bf9260048083019391928290030181865afa158015612431573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124559190614bc2565b6000838152600e602052604090205461246e9190614ca5565b90508034101561249a57604051634fd394c160e11b8152346004820152602481018290526044016115d1565b6124a385613eb6565b6124ac86613ed7565b6124b584613ed7565b836001600160a01b0316866001600160a01b0316036124e757604051630df7483960e41b815260040160405180910390fd5b604080516002808252606082018352600092602083019080368337019050509050868160008151811061251c5761251c614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050848160018151811061255057612550614cbc565b60200260200101906001600160a01b031690816001600160a01b0316815250506125798161401e565b61258281613fde565b61258b81614231565b604080516001808252818301909252600091602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337505060408051600180825281830190925292935060009291506020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090508a8460008151811061262a5761262a614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050898360008151811061265e5761265e614cbc565b602002602001018181525050888260008151811061267e5761267e614cbc565b6001600160a01b039092166020928302919091018201526040805161010081018252600d5463ffffffff600160a01b90910416815233928101929092528101859052606081018490526080810183905260a08101829052600060c082015260e081018990526126ec816140c5565b7f806ee47a6104a6e7ede1e15a9c9f384c0706b79fcea8a9edb136fe3b51b496b6600d60149054906101000a900463ffffffff168260405161272f929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff1690601461275483614cf1565b91906101000a81548163ffffffff021916908363ffffffff16021790555050505050505050505050505050565b3360009081526005602052604090205460ff166127b0576040516282b42960e81b815260040160405180910390fd5b600a54816001600160801b031610156127e757604051632797027760e01b81526001600160801b03821660048201526024016115d1565b60006127f38242614d3b565b6001600160a01b0384166000908152600b60205260409020549091506001600160801b03908116908216811061284f57604051630a4dc5d360e31b81526001600160801b038083166004830152831660248201526044016115d1565b6001600160a01b0384166000818152600b602090815260409182902080546001600160801b0319166001600160801b0387169081179091558251938452908301527f6a8b3a105d0f4bca0c6d25f6f59282c2cac3e2acdd3f3466dabe9725f3299703910160405180910390a150505050565b3360009081526005602052604090205460ff166128f0576040516282b42960e81b815260040160405180910390fd5b6001600160a01b0381166000908152600b60205260409020546001600160801b034281169116101561294057604051637407be0b60e11b81526001600160a01b03821660048201526024016115d1565b6001600160a01b0381166000818152600b602090815260409182902080546001600160801b031916905590519182527f975fea2c466fdf1d3e8dd9969713fbfde0822e5b3921c18c0003e033f216bf5a910160405180910390a150565b6002546001600160a01b031633146129c7576040516282b42960e81b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b6002546001600160a01b03163314612a13576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19169055517f4d6789b83afc897fab8472d33d3f7cc1fba254215a1cd007ce51f3c5bf6f57729190a250565b600d54600160d01b900460ff1615612a87576040516318c0a57b60e31b815260040160405180910390fd5b6000805260076020527f6d5257204ebe7d88fd91ae87941cb2dd9d8062b64ae5a2bd2d28ec40b9fbf6df5460ff16158015612ad257503360009081526007602052604090205460ff16155b15612aef576040516282b42960e81b815260040160405180910390fd5b6004546001600160a01b031615801590612b1457506004546001600160a01b03163314155b15612b31576040516282b42960e81b815260040160405180910390fd5b600082612b6c57612b4184613d60565b604051602001612b519190614d5b565b60405160208183030381529060405280519060200120612b9c565b612b7584613d60565b604051602001612b859190614d94565b604051602081830303815290604052805190602001205b6000818152600e60205260409020549091508315612cd657604051630311ead160e61b815233600482015260009081906001600160a01b037f000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1169063c47ab44090602401602060405180830381865afa158015612c1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c419190614dcf565b6001600160a01b031614612c56576001612c59565b60005b905080612cd057600e6000604051602001612c88906d283937bc3cafa1b932b0ba34b7b760911b8152600e0190565b60405160208183030381529060405280519060200120815260200190815260200160002054600e600085815260200190815260200160002054612ccb9190614c10565b612cd2565b815b9150505b600d60009054906101000a90046001600160a01b03166001600160a01b031663e3a031bf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d4d9190614bc2565b612d579082614ca5565b905080341015612d8357604051634fd394c160e11b8152346004820152602481018290526044016115d1565b612d8c85613e8b565b6000856001600160401b03811115612da657612da661474d565b604051908082528060200260200182016040528015612dcf578160200160208202803683370190505b5090506000866001600160401b03811115612dec57612dec61474d565b604051908082528060200260200182016040528015612e15578160200160208202803683370190505b50905060005b87811015612ef257612e3889898381811061168f5761168f614cbc565b612e4d8989838181106116b3576116b3614cbc565b888882818110612e5f57612e5f614cbc565b612e759260206040909202019081019150614544565b838281518110612e8757612e87614cbc565b60200260200101906001600160a01b031690816001600160a01b031681525050888882818110612eb957612eb9614cbc565b90506040020160200135828281518110612ed557612ed5614cbc565b602090810291909101015280612eea81614c52565b915050612e1b565b50612efd8282613fbc565b612f0682613efe565b612f0f8261401e565b612f1882613fde565b8515612f2757612f27826142d8565b6040805161010081018252600d54600160a01b900463ffffffff168152336020820152908101839052606081018290526000906080810182604051908082528060200260200182016040528015612f88578160200160208202803683370190505b5081526020016000604051908082528060200260200182016040528015612fb9578160200160208202803683370190505b50815288151560208201526040018790529050612fd5816140c5565b7f8f1d75282d5f6e805e4525ffa180022a0be9390ae16b9391e8bae3b0f85e39bf600d60149054906101000a900463ffffffff1682604051613018929190614cd2565b60405180910390a1600d8054600160a01b900463ffffffff1690601461303d83614cf1565b91906101000a81548163ffffffff021916908363ffffffff16021790555050505050505050505050565b61306f6143ed565b600080546001919061308b9085906001600160401b0316614dec565b6001600160401b031681526020808201929092526040908101600020815161010081018352815463ffffffff8116825264010000000090046001600160a01b03168185015260018201805484518187028101870186528181529295939486019383018282801561312457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613106575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561317c57602002820191906000526020600020905b815481526020019060010190808311613168575b50505050508152602001600382018054806020026020016040519081016040528092919081815260200182805480156131de57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116131c0575b505050505081526020016004820180548060200260200160405190810160405280929190818152602001828054801561323657602002820191906000526020600020905b815481526020019060010190808311613222575b5050509183525050600582015460ff161515602082015260069091015460409091015292915050565b6002546001600160a01b03163314613289576040516282b42960e81b815260040160405180910390fd5b60005b81811015611aa0576001600760008585858181106132ac576132ac614cbc565b90506020020160208101906132c19190614544565b6001600160a01b031681526020810191909152604001600020805460ff19169115159190911790558282828181106132fb576132fb614cbc565b90506020020160208101906133109190614544565b6001600160a01b03167f19ef9a4877199f89440a26acb26895ec02ed86f2df1aeaa90dc18041b892f71f60405160405180910390a261334e81614c52565b905061328c565b600080836040516020016133699190614e0c565b60408051601f1981840301815291815281516020928301206000818152600e90935291205490915083156134b957604051630311ead160e61b815233600482015260009081906001600160a01b037f000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1169063c47ab44090602401602060405180830381865afa158015613400573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134249190614dcf565b6001600160a01b03161461343957600161343c565b60005b9050806134b357600e600060405160200161346b906d283937bc3cafa1b932b0ba34b7b760911b8152600e0190565b60405160208183030381529060405280519060200120815260200190815260200160002054600e6000858152602001908152602001600020546134ae9190614c10565b6134b5565b815b9150505b600d60009054906101000a90046001600160a01b03166001600160a01b031663e3a031bf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561350c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135309190614bc2565b61353a9082614ca5565b925050505b92915050565b3360009081526005602052604090205460ff16613574576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d01b1916600160d01b1790556040517f68b095021b1f40fe513109f513c66692f0b3219aee674a69f4efc57badb8201d90610cda9042815260200190565b6002546001600160a01b031633146135e3576040516282b42960e81b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0383169081179091556040517fc720a84179457777bd6ab883473f5ae8b0d37c9d100fd66f070ddcbd35af020f90600090a250565b3360009081526006602052604090205460ff1661365c576040516282b42960e81b815260040160405180910390fd5b600c546040516339e1f2a960e21b8152600481018390526001600160a01b039091169063e787caa490602401600060405180830381600087803b1580156136a257600080fd5b505af11580156136b6573d6000803e3d6000fd5b5050505050565b6002546001600160a01b031633146136e7576040516282b42960e81b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b3360009081526005602052604090205460ff16613738576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d81b1916600160d81b1790556040517f68b095021b1f40fe513109f513c66692f0b3219aee674a69f4efc57badb8201d90610cda9042815260200190565b6002546001600160a01b031633146137a7576040516282b42960e81b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b03831690811790915560405133907f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270090600090a350565b6002546001600160a01b0316331461381d576040516282b42960e81b815260040160405180910390fd5b60405147906000906001600160a01b0384169083908381818185875af1925050503d806000811461386a576040519150601f19603f3d011682016040523d82523d6000602084013e61386f565b606091505b5050905080611aa057600080fd5b6000805b8251811015613900576138d183828151811061389f5761389f614cbc565b60200260200101516001600160a01b03166000908152600b60205260409020546001600160801b034281169116101590565b156138f8578281815181106138e8576138e8614cbc565b6020026020010151915050919050565b600101613881565b50600092915050565b3360009081526005602052604090205460ff16613938576040516282b42960e81b815260040160405180910390fd5b600d805460ff60d01b191690556040514281527faaa520fdd7d2c83061d632fa017b0432407e798818af63ea908589fceda39ab790602001610cda565b61397d6143ed565b600080546001600160401b0316818061399583614e28565b91906101000a8154816001600160401b0302191690836001600160401b0316021790555090506000800160089054906101000a90046001600160401b03166001600160401b0316816001600160401b031603613a04576040516331dcf2b560e01b815260040160405180910390fd5b6001600160401b0381166000908152600160208181526040808420815161010081018352815463ffffffff8116825264010000000090046001600160a01b03168185015293810180548351818602810186018552818152929493860193830182828015613a9a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613a7c575b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015613af257602002820191906000526020600020905b815481526020019060010190808311613ade575b5050505050815260200160038201805480602002602001604051908101604052809291908181526020018280548015613b5457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613b36575b5050505050815260200160048201805480602002602001604051908101604052809291908181526020018280548015613bac57602002820191906000526020600020905b815481526020019060010190808311613b98575b5050509183525050600582015460ff1615156020808301919091526006909201546040918201526001600160401b038516600090815260019283905290812080546001600160c01b031916815592935090613c0990830182614443565b613c17600283016000614443565b613c25600383016000614443565b613c33600483016000614443565b5060058101805460ff19169055600060069091015592915050565b613c5661197b565b816001600160401b03161115613c7f5760405163ad2ce56160e01b815260040160405180910390fd5b600080546001600160401b031690613c978383614dec565b90505b806001600160401b0316826001600160401b03161015613d3b576001600160401b0382166000908152600160208190526040822080546001600160c01b03191681559190613cea90830182614443565b613cf8600283016000614443565b613d06600383016000614443565b613d14600483016000614443565b5060058101805460ff19169055600060069091015581613d3381614e28565b925050613c9a565b506000805467ffffffffffffffff19166001600160401b039290921691909117905550565b606081600003613d875750506040805180820190915260018152600360fc1b602082015290565b8160005b8115613db15780613d9b81614c52565b9150613daa9050600a83614e44565b9150613d8b565b6000816001600160401b03811115613dcb57613dcb61474d565b6040519080825280601f01601f191660200182016040528015613df5576020820181803683370190505b509050815b8515613e8257613e0b600182614c23565b90506000613e1a600a88614e44565b613e2590600a614ca5565b613e2f9088614c23565b613e3a906030614e66565b905060008160f81b905080848481518110613e5757613e57614cbc565b60200101906001600160f81b031916908160001a905350613e79600a89614e44565b97505050613dfa565b50949350505050565b6005811180613e98575080155b1561130c57604051631d7317ad60e31b815260040160405180910390fd5b8060000361130c5760405163339d6ef160e21b815260040160405180910390fd5b6001600160a01b03811661130c5760405163339d6ef160e21b815260040160405180910390fd5b60005b60018251613f0f9190614c23565b811015613fb8576000613f23826001614c10565b90505b8251811015613fa557828181518110613f4157613f41614cbc565b60200260200101516001600160a01b0316838381518110613f6457613f64614cbc565b60200260200101516001600160a01b031603613f9357604051630df7483960e41b815260040160405180910390fd5b80613f9d81614c52565b915050613f26565b5080613fb081614c52565b915050613f01565b5050565b8051825114613fb85760405163b1f40f7760e01b815260040160405180910390fd5b6000613fe98261387d565b90506001600160a01b03811615613fb85760405163fc31f2eb60e01b81526001600160a01b03821660048201526024016115d1565b600c5460405163db10e60f60e01b81526000916001600160a01b03169063db10e60f9061404f908590600401614e7f565b602060405180830381865afa15801561406c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140909190614dcf565b90506001600160a01b03811615613fb857604051632777a68f60e11b81526001600160a01b03821660048201526024016115d1565b600080548291600191600160401b90046001600160401b03168160086140ea83614e28565b82546101009290920a6001600160401b038181021990931691831602179091551681526020808201929092526040908101600020835181548585015163ffffffff9092166001600160c01b0319909116176401000000006001600160a01b0390921691909102178155908301518051919261416d92600185019290910190614461565b50606082015180516141899160028401916020909101906144c6565b50608082015180516141a5916003840191602090910190614461565b5060a082015180516141c19160048401916020909101906144c6565b5060c082015160058201805460ff191691151591909117905560e09091015160069091015550565b6000806141f6858461439d565b9050600061420486856143ce565b90508185108061421357508085115b156142235760009250505061422a565b6001925050505b9392505050565b600c5460405163aeabb17f60e01b81526000916001600160a01b03169063aeabb17f90614262908590600401614e7f565b602060405180830381865afa15801561427f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142a39190614dcf565b90506001600160a01b03811615613fb8576040516303e4acb760e01b81526001600160a01b03821660048201526024016115d1565b60405163eb2fea2d60e01b81526000906001600160a01b037f000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1169063eb2fea2d90614327908590600401614e7f565b602060405180830381865afa158015614344573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143689190614dcf565b90506001600160a01b03811615613fb85760405163b3ef117360e01b81526001600160a01b03821660048201526024016115d1565b612710818103908211611388198290048411820217156143bc57600080fd5b61271092026113880191909104919050565b612710810161271019821161138819829004841117156143bc57600080fd5b604051806101000160405280600063ffffffff16815260200160006001600160a01b0316815260200160608152602001606081526020016060815260200160608152602001600015158152602001600081525090565b508054600082559060005260206000209081019061130c9190614501565b8280548282559060005260206000209081019282156144b6579160200282015b828111156144b657825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614481565b506144c2929150614501565b5090565b8280548282559060005260206000209081019282156144b6579160200282015b828111156144b65782518255916020019190600101906144e6565b5b808211156144c25760008155600101614502565b60006020828403121561452857600080fd5b5035919050565b6001600160a01b038116811461130c57600080fd5b60006020828403121561455657600080fd5b813561422a8161452f565b6000806040838503121561457457600080fd5b50508035926020909101359150565b60006020828403121561459557600080fd5b81356001600160401b038116811461422a57600080fd5b60005b838110156145c75781810151838201526020016145af565b50506000910152565b600081518084526145e88160208601602086016145ac565b601f01601f19169290920160200192915050565b821515815260406020820152600061461760408301846145d0565b949350505050565b60008083601f84011261463157600080fd5b5081356001600160401b0381111561464857600080fd5b6020830191508360208260061b850101111561466357600080fd5b9250929050565b60008060006040848603121561467f57600080fd5b83356001600160401b0381111561469557600080fd5b6146a18682870161461f565b909790965060209590950135949350505050565b600080602083850312156146c857600080fd5b82356001600160401b03808211156146df57600080fd5b818501915085601f8301126146f357600080fd5b81358181111561470257600080fd5b8660208260051b850101111561471757600080fd5b60209290920196919550909350505050565b60006020828403121561473b57600080fd5b813561ffff8116811461422a57600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561478b5761478b61474d565b604052919050565b600080600083850360808112156147a957600080fd5b60408112156147b757600080fd5b50604051604081018181106001600160401b03821117156147da576147da61474d565b60405284356147e88161452f565b815260208581013590820152925060408401356148048161452f565b929592945050506060919091013590565b6000806000806080858703121561482b57600080fd5b84356148368161452f565b935060208501359250604085013561484d8161452f565b9396929550929360600135925050565b6000806040838503121561487057600080fd5b823561487b8161452f565b915060208301356001600160801b038116811461489757600080fd5b809150509250929050565b803580151581146148b257600080fd5b919050565b600080600080606085870312156148cd57600080fd5b84356001600160401b038111156148e357600080fd5b6148ef8782880161461f565b90955093506149029050602086016148a2565b9396929550929360400135925050565b600081518084526020808501945080840160005b8381101561494b5781516001600160a01b031687529582019590820190600101614926565b509495945050505050565b600081518084526020808501945080840160005b8381101561494b5781518752958201959082019060010161496a565b805163ffffffff168252600061010060208301516149af60208601826001600160a01b03169052565b5060408301518160408601526149c782860182614912565b915050606083015184820360608601526149e18282614956565b915050608083015184820360808601526149fb8282614912565b91505060a083015184820360a0860152614a158282614956565b91505060c0830151614a2b60c086018215159052565b5060e083015160e08501528091505092915050565b60208152600061422a6020830184614986565b60008060408385031215614a6657600080fd5b82356001600160401b0380821115614a7d57600080fd5b818501915085601f830112614a9157600080fd5b8135602082821115614aa557614aa561474d565b614ab7601f8301601f19168201614763565b92508183528781838601011115614acd57600080fd5b81818501828501376000818385010152829550614aeb8188016148a2565b9450505050509250929050565b60006020808385031215614b0b57600080fd5b82356001600160401b0380821115614b2257600080fd5b818501915085601f830112614b3657600080fd5b813581811115614b4857614b4861474d565b8060051b9150614b59848301614763565b8181529183018401918481019088841115614b7357600080fd5b938501935b83851015614b9d5784359250614b8d8361452f565b8282529385019390850190614b78565b98975050505050505050565b8281526040602082015260006146176040830184614986565b600060208284031215614bd457600080fd5b5051919050565b63ffffffff8316815260406020820152600061461760408301846145d0565b634e487b7160e01b600052601160045260246000fd5b8082018082111561353f5761353f614bfa565b8181038181111561353f5761353f614bfa565b6000600160ff1b8201614c4b57614c4b614bfa565b5060000390565b600060018201614c6457614c64614bfa565b5060010190565b7157697468647261775f5374616e646172645f60701b815260008251614c988160128501602087016145ac565b9190910160120192915050565b808202811582820484141761353f5761353f614bfa565b634e487b7160e01b600052603260045260246000fd5b63ffffffff831681526040602082015260006146176040830184614986565b600063ffffffff808316818103614d0a57614d0a614bfa565b6001019392505050565b6001600160401b03828116828216039080821115614d3457614d34614bfa565b5092915050565b6001600160801b03818116838216019080821115614d3457614d34614bfa565b704465706f7369745f5374616e646172645f60781b815260008251614d878160118501602087016145ac565b9190910160110192915050565b724465706f7369745f476f7665726e616e63655f60681b815260008251614dc28160138501602087016145ac565b9190910160130192915050565b600060208284031215614de157600080fd5b815161422a8161452f565b6001600160401b03818116838216019080821115614d3457614d34614bfa565b60008251614e1e8184602087016145ac565b9190910192915050565b60006001600160401b03808316818103614d0a57614d0a614bfa565b600082614e6157634e487b7160e01b600052601260045260246000fd5b500490565b60ff818116838216019081111561353f5761353f614bfa565b60208152600061422a602083018461491256fea2646970667358221220d0814db886cb03bb1e8f13667a8fb195ba1e886c5131e80b719c8d76e36df0b764736f6c63430008130033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000009b122361e8708be33b785e44fce4d6ca86ab6c5a000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000012b79b4593a13085d73229968d1b867c62a32331077e69c330a19117713527cd3693f2818dadb98b16d09e2e242225f069ec196a1ccaac86c5e21a6a7dc211e7a64975a0313faffb91d1d3bdb7733fce82fc79c31dbce3725a7bd30a4c0309dd5430f5e54773ee1f907ea369134d807f2f5e9a077a596ae33f202ee9efd8a79041f06650d97d5fa110714d9ba428a531bcce2994ecf368233773b269c33c5284c0a5d57abcb8c5cffcc475879522d4079eb000aab40af8c8dfa2c6825a4ec7fa8e75a5f64a69880afad6dc81260417ab82e188266bb12f38be7aa677f6951f46a66284fc93b15bdbaa3497df91f72b049f9391b0a5cdb0c70261858cd800e78ef8295e35ff4b9162c303c6b01738b2b55433e7738994defbd6b415a61d44ab34adccaa06416913f4262df5915ac4295960f5c2ee4a97453e8ecd22441a0a7ed1721330c7e431ba9511c00ecdb3bd76484b80368e8cdb3ad45b7a38620e42645b4a1139c37a0006eb1553293885ecb8074975c26e075b1415e0bb8e4d1a6ff6879bbfef723e9aab4abce7940c28392120fc89b0eb7dfefd757fd1437a57bd866b1bb04d9a2639a5b4cf65e1bb56c25943b4c18a01953da0b432c33c871422bd44b00373e344542a62092609e9d9f47788dd56d305552e13b39ee9458cdd3ca862d5cd646213e4389ec59b9bb8d0b5ea7574d143ad62171d149c5ac0da897d7efef56109d294b2402434fc7c2c34271ecc470039cb01a6946c8658989975ef72be6dbce316f0d9d2a3affa97de1d99bb2aac0538e2666d0d8545545ead241ef0ccab00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c00000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c00000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000061a80000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000927c000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000061a80

-----Decoded View---------------
Arg [0] : _oracleModule (address): 0x9B122361E8708Be33B785e44FcE4d6ca86AB6C5a
Arg [1] : _govModule (address): 0xc6F50903a058f3807111619bD4B24cA64b8239E1
Arg [2] : _deviationThreshold (uint8): 100
Arg [3] : _relayerActions (bytes32[]): System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[],System.Byte[]
Arg [4] : _gasUsages (uint256[]): 200000,300000,400000,500000,600000,200000,300000,400000,500000,600000,200000,300000,400000,500000,600000,300000,100000,400000

-----Encoded View---------------
43 Constructor Arguments found :
Arg [0] : 0000000000000000000000009b122361e8708be33b785e44fce4d6ca86ab6c5a
Arg [1] : 000000000000000000000000c6f50903a058f3807111619bd4b24ca64b8239e1
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000064
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000300
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [6] : b79b4593a13085d73229968d1b867c62a32331077e69c330a19117713527cd36
Arg [7] : 93f2818dadb98b16d09e2e242225f069ec196a1ccaac86c5e21a6a7dc211e7a6
Arg [8] : 4975a0313faffb91d1d3bdb7733fce82fc79c31dbce3725a7bd30a4c0309dd54
Arg [9] : 30f5e54773ee1f907ea369134d807f2f5e9a077a596ae33f202ee9efd8a79041
Arg [10] : f06650d97d5fa110714d9ba428a531bcce2994ecf368233773b269c33c5284c0
Arg [11] : a5d57abcb8c5cffcc475879522d4079eb000aab40af8c8dfa2c6825a4ec7fa8e
Arg [12] : 75a5f64a69880afad6dc81260417ab82e188266bb12f38be7aa677f6951f46a6
Arg [13] : 6284fc93b15bdbaa3497df91f72b049f9391b0a5cdb0c70261858cd800e78ef8
Arg [14] : 295e35ff4b9162c303c6b01738b2b55433e7738994defbd6b415a61d44ab34ad
Arg [15] : ccaa06416913f4262df5915ac4295960f5c2ee4a97453e8ecd22441a0a7ed172
Arg [16] : 1330c7e431ba9511c00ecdb3bd76484b80368e8cdb3ad45b7a38620e42645b4a
Arg [17] : 1139c37a0006eb1553293885ecb8074975c26e075b1415e0bb8e4d1a6ff6879b
Arg [18] : bfef723e9aab4abce7940c28392120fc89b0eb7dfefd757fd1437a57bd866b1b
Arg [19] : b04d9a2639a5b4cf65e1bb56c25943b4c18a01953da0b432c33c871422bd44b0
Arg [20] : 0373e344542a62092609e9d9f47788dd56d305552e13b39ee9458cdd3ca862d5
Arg [21] : cd646213e4389ec59b9bb8d0b5ea7574d143ad62171d149c5ac0da897d7efef5
Arg [22] : 6109d294b2402434fc7c2c34271ecc470039cb01a6946c8658989975ef72be6d
Arg [23] : bce316f0d9d2a3affa97de1d99bb2aac0538e2666d0d8545545ead241ef0ccab
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [25] : 0000000000000000000000000000000000000000000000000000000000030d40
Arg [26] : 00000000000000000000000000000000000000000000000000000000000493e0
Arg [27] : 0000000000000000000000000000000000000000000000000000000000061a80
Arg [28] : 000000000000000000000000000000000000000000000000000000000007a120
Arg [29] : 00000000000000000000000000000000000000000000000000000000000927c0
Arg [30] : 0000000000000000000000000000000000000000000000000000000000030d40
Arg [31] : 00000000000000000000000000000000000000000000000000000000000493e0
Arg [32] : 0000000000000000000000000000000000000000000000000000000000061a80
Arg [33] : 000000000000000000000000000000000000000000000000000000000007a120
Arg [34] : 00000000000000000000000000000000000000000000000000000000000927c0
Arg [35] : 0000000000000000000000000000000000000000000000000000000000030d40
Arg [36] : 00000000000000000000000000000000000000000000000000000000000493e0
Arg [37] : 0000000000000000000000000000000000000000000000000000000000061a80
Arg [38] : 000000000000000000000000000000000000000000000000000000000007a120
Arg [39] : 00000000000000000000000000000000000000000000000000000000000927c0
Arg [40] : 00000000000000000000000000000000000000000000000000000000000493e0
Arg [41] : 00000000000000000000000000000000000000000000000000000000000186a0
Arg [42] : 0000000000000000000000000000000000000000000000000000000000061a80


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.