ETH Price: $2,421.60 (-1.01%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00
Transaction Hash
Method
Block
From
To
Multicall210643882024-10-28 13:19:477 days ago1730121587IN
0x6C615481...a85d13659
0 ETH0.0019656610.30297139
Multicall210560212024-10-27 9:18:358 days ago1730020715IN
0x6C615481...a85d13659
0 ETH0.002366387.8826043
Multicall210559952024-10-27 9:13:238 days ago1730020403IN
0x6C615481...a85d13659
0 ETH0.002717978.76005146
Multicall209981932024-10-19 7:42:5916 days ago1729323779IN
0x6C615481...a85d13659
4 ETH0.002554428.80596081
Multicall209721232024-10-15 16:21:3520 days ago1729009295IN
0x6C615481...a85d13659
0 ETH0.0042604721.24119407
Multicall209647132024-10-14 15:31:3521 days ago1728919895IN
0x6C615481...a85d13659
0 ETH0.0070931538.1
Multicall208852082024-10-03 13:12:2332 days ago1727961143IN
0x6C615481...a85d13659
0 ETH0.00122377.41814426
Multicall208530752024-09-29 1:40:4736 days ago1727574047IN
0x6C615481...a85d13659
0 ETH0.002002535.85746251
Multicall208463632024-09-28 3:11:3537 days ago1727493095IN
0x6C615481...a85d13659
0.5 ETH0.004964311.12159808
Multicall208317702024-09-26 2:22:4739 days ago1727317367IN
0x6C615481...a85d13659
0 ETH0.003543318.09990433
Multicall208191762024-09-24 8:11:3541 days ago1727165495IN
0x6C615481...a85d13659
0 ETH0.0076198131.39420192
Multicall208191722024-09-24 8:10:4741 days ago1727165447IN
0x6C615481...a85d13659
0 ETH0.0099330434.26227626
Multicall207620172024-09-16 8:32:5949 days ago1726475579IN
0x6C615481...a85d13659
0 ETH0.001807269.05935289
Multicall207357412024-09-12 16:31:5953 days ago1726158719IN
0x6C615481...a85d13659
0 ETH0.001049185.75372822
Multicall207342422024-09-12 11:29:5953 days ago1726140599IN
0x6C615481...a85d13659
0 ETH0.000722432.13454512
Multicall207341892024-09-12 11:19:2353 days ago1726139963IN
0x6C615481...a85d13659
0.00426202 ETH0.000483621.98369898
Multicall206720732024-09-03 19:14:1161 days ago1725390851IN
0x6C615481...a85d13659
0 ETH0.000492531.52935981
Multicall206688312024-09-03 8:23:3562 days ago1725351815IN
0x6C615481...a85d13659
0 ETH0.000405151.57419634
Multicall206215572024-08-27 17:56:5968 days ago1724781419IN
0x6C615481...a85d13659
0 ETH0.000255911.45703243
Multicall206130452024-08-26 13:24:2370 days ago1724678663IN
0x6C615481...a85d13659
0 ETH0.000478852.40040634
Multicall205273172024-08-14 13:58:1182 days ago1723643891IN
0x6C615481...a85d13659
0 ETH0.002414039.36658037
Multicall204946052024-08-10 0:24:2386 days ago1723249463IN
0x6C615481...a85d13659
0 ETH0.000400711.4754427
Multicall204856732024-08-08 18:30:4787 days ago1723141847IN
0x6C615481...a85d13659
0 ETH0.001338686.43729417
Multicall204653422024-08-05 22:26:5990 days ago1722896819IN
0x6C615481...a85d13659
0 ETH0.000952163.34764514
Multicall203838472024-07-25 13:25:23102 days ago1721913923IN
0x6C615481...a85d13659
0 ETH0.00281548.54270936
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
209981932024-10-19 7:42:5916 days ago1729323779
0x6C615481...a85d13659
4 ETH
209647132024-10-14 15:31:3521 days ago1728919895
0x6C615481...a85d13659
0.3125 ETH
209647132024-10-14 15:31:3521 days ago1728919895
0x6C615481...a85d13659
0.3125 ETH
208852082024-10-03 13:12:2332 days ago1727961143
0x6C615481...a85d13659
0.90886681 ETH
208852082024-10-03 13:12:2332 days ago1727961143
0x6C615481...a85d13659
0.90886681 ETH
208463632024-09-28 3:11:3537 days ago1727493095
0x6C615481...a85d13659
0.5 ETH
207620172024-09-16 8:32:5949 days ago1726475579
0x6C615481...a85d13659
0.01402814 ETH
207620172024-09-16 8:32:5949 days ago1726475579
0x6C615481...a85d13659
0.01402814 ETH
207357412024-09-12 16:31:5953 days ago1726158719
0x6C615481...a85d13659
3.75094764 ETH
207357412024-09-12 16:31:5953 days ago1726158719
0x6C615481...a85d13659
3.75094764 ETH
207341892024-09-12 11:19:2353 days ago1726139963
0x6C615481...a85d13659
0.00426202 ETH
206130452024-08-26 13:24:2370 days ago1724678663
0x6C615481...a85d13659
1.66180008 ETH
206130452024-08-26 13:24:2370 days ago1724678663
0x6C615481...a85d13659
1.66180008 ETH
203588122024-07-22 1:32:47105 days ago1721611967
0x6C615481...a85d13659
0.07117834 ETH
203588122024-07-22 1:32:47105 days ago1721611967
0x6C615481...a85d13659
0.07117834 ETH
202653222024-07-09 0:21:35118 days ago1720484495
0x6C615481...a85d13659
1 ETH
202645582024-07-08 21:47:35118 days ago1720475255
0x6C615481...a85d13659
0.01 ETH
202266952024-07-03 14:51:47124 days ago1720018307
0x6C615481...a85d13659
0.0016 ETH
202160332024-07-02 3:04:47125 days ago1719889487
0x6C615481...a85d13659
0.0015 ETH
202074722024-06-30 22:24:23126 days ago1719786263
0x6C615481...a85d13659
0.004 ETH
202029482024-06-30 7:16:11127 days ago1719731771
0x6C615481...a85d13659
0.011 ETH
202019432024-06-30 3:54:11127 days ago1719719651
0x6C615481...a85d13659
0.0015 ETH
202010292024-06-30 0:49:23127 days ago1719708563
0x6C615481...a85d13659
0.00166666 ETH
202010292024-06-30 0:49:23127 days ago1719708563
0x6C615481...a85d13659
0.00166666 ETH
202005052024-06-29 23:04:35127 days ago1719702275
0x6C615481...a85d13659
0.0015 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DCAHubCompanion

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 9999 runs

Other Settings:
paris EvmVersion
File 1 of 31 : DCAHubCompanion.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.22;

import './DCAHubCompanionLibrariesHandler.sol';
import './DCAHubCompanionHubProxyHandler.sol';
import '../utils/BaseCompanion.sol';

contract DCAHubCompanion is DCAHubCompanionLibrariesHandler, DCAHubCompanionHubProxyHandler, BaseCompanion, IDCAHubCompanion {
  constructor(
    address _swapper,
    address _allowanceTarget,
    address _governor,
    IPermit2 _permit2
  ) BaseCompanion(_swapper, _allowanceTarget, _governor, _permit2) {}
}

File 2 of 31 : SimulationAdapter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

import { IERC165 } from "./interfaces/external/IERC165.sol";
import { ISimulationAdapter } from "./interfaces/ISimulationAdapter.sol";

/**
 * @title Simulation Adapter
 * @author Sam Bugs
 * @notice This contracts adds off-chain simulation capabilities to existing contracts. It works similarly to a
 *         multicall, but the state is not modified in each subcall.
 */
abstract contract SimulationAdapter is IERC165, ISimulationAdapter {
  /// @notice An error that contains a simulation's result
  error SimulatedCall(SimulationResult result);

  /// @inheritdoc IERC165
  function supportsInterface(bytes4 _interfaceId) public view virtual override returns (bool) {
    return _interfaceId == type(ISimulationAdapter).interfaceId || _interfaceId == type(IERC165).interfaceId;
  }

  /// @inheritdoc ISimulationAdapter
  function simulate(bytes[] calldata _calls) external payable returns (SimulationResult[] memory _results) {
    _results = new SimulationResult[](_calls.length);
    for (uint256 i = 0; i < _calls.length; i++) {
      _results[i] = _simulate(_calls[i]);
    }
    return _results;
  }

  /**
   * @notice Executes a simulation and returns the result
   * @param _call The call to simulate
   * @return _simulationResult The simulation's result
   */
  function _simulate(bytes calldata _call) internal returns (SimulationResult memory _simulationResult) {
    (bool _success, bytes memory _result) =
    // solhint-disable-next-line avoid-low-level-calls
     address(this).delegatecall(abi.encodeWithSelector(this.simulateAndRevert.selector, _call));
    require(!_success, "WTF? Should have failed!");
    // Move pointer to ignore selector
    // solhint-disable-next-line no-inline-assembly
    assembly {
      _result := add(_result, 0x04)
    }
    (_simulationResult) = abi.decode(_result, (SimulationResult));
  }

  /**
   * @notice Executes a call agains this contract and reverts with the result
   * @dev This is meant to be used internally, do not call!
   * @param _call The call to simulate
   */
  function simulateAndRevert(bytes calldata _call) external payable {
    uint256 _gasAtStart = gasleft();
    // solhint-disable-next-line avoid-low-level-calls
    (bool _success, bytes memory _result) = address(this).delegatecall(_call);
    uint256 _gasSpent = _gasAtStart - gasleft();
    revert SimulatedCall(SimulationResult({ success: _success, result: _result, gasSpent: _gasSpent }));
  }
}

File 3 of 31 : ISimulationAdapter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

interface ISimulationAdapter {
  /// @notice A simulation's result
  struct SimulationResult {
    bool success;
    bytes result;
    uint256 gasSpent;
  }

  /**
   * @notice Executes individual simulations against this contract but doesn't modify the state when doing so
   * @dev This function is meant to be used for off-chain simulation and should not be called on-chain
   * @param calls The calls to simulate
   * @return results Each simulation result
   */
  function simulate(bytes[] calldata calls) external payable returns (SimulationResult[] memory results);
}

File 4 of 31 : IERC165.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

interface IERC165 {
  /**
   * @dev Returns true if this contract implements the interface defined by
   * `interfaceId`. See the corresponding
   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
   * to learn more about how these ids are created.
   *
   * This function call must use less than 30 000 gas.
   */
  function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 5 of 31 : IDCAHub.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import '@mean-finance/oracles/solidity/interfaces/ITokenPriceOracle.sol';
import './IDCAPermissionManager.sol';

/**
 * @title The interface for all state related queries
 * @notice These methods allow users to read the hubs's current values
 */
interface IDCAHubParameters {
  /**
   * @notice Returns how much will the amount to swap differ from the previous swap. f.e. if the returned value is -100, then the amount to swap will be 100 less than the swap just before it
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA One of the pair's token
   * @param tokenB The other of the pair's token
   * @param swapIntervalMask The byte representation of the swap interval to check
   * @param swapNumber The swap number to check
   * @return swapDeltaAToB How much less of token A will the following swap require
   * @return swapDeltaBToA How much less of token B will the following swap require
   */
  function swapAmountDelta(
    address tokenA,
    address tokenB,
    bytes1 swapIntervalMask,
    uint32 swapNumber
  ) external view returns (uint128 swapDeltaAToB, uint128 swapDeltaBToA);

  /**
   * @notice Returns the sum of the ratios reported in all swaps executed until the given swap number
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA One of the pair's token
   * @param tokenB The other of the pair's token
   * @param swapIntervalMask The byte representation of the swap interval to check
   * @param swapNumber The swap number to check
   * @return accumRatioAToB The sum of all ratios from A to B
   * @return accumRatioBToA The sum of all ratios from B to A
   */
  function accumRatio(
    address tokenA,
    address tokenB,
    bytes1 swapIntervalMask,
    uint32 swapNumber
  ) external view returns (uint256 accumRatioAToB, uint256 accumRatioBToA);

  /**
   * @notice Returns swapping information about a specific pair
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA One of the pair's token
   * @param tokenB The other of the pair's token
   * @param swapIntervalMask The byte representation of the swap interval to check
   * @return performedSwaps How many swaps have been executed
   * @return nextAmountToSwapAToB How much of token A will be swapped on the next swap
   * @return lastSwappedAt Timestamp of the last swap
   * @return nextAmountToSwapBToA How much of token B will be swapped on the next swap
   */
  function swapData(
    address tokenA,
    address tokenB,
    bytes1 swapIntervalMask
  )
    external
    view
    returns (
      uint32 performedSwaps,
      uint224 nextAmountToSwapAToB,
      uint32 lastSwappedAt,
      uint224 nextAmountToSwapBToA
    );

  /**
   * @notice Returns the byte representation of the set of actice swap intervals for the given pair
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA The smaller of the pair's token
   * @param tokenB The other of the pair's token
   * @return The byte representation of the set of actice swap intervals
   */
  function activeSwapIntervals(address tokenA, address tokenB) external view returns (bytes1);

  /**
   * @notice Returns how much of the hub's token balance belongs to the platform
   * @param token The token to check
   * @return The amount that belongs to the platform
   */
  function platformBalance(address token) external view returns (uint256);
}

/**
 * @title The interface for all position related matters
 * @notice These methods allow users to create, modify and terminate their positions
 */
interface IDCAHubPositionHandler {
  /// @notice The position of a certain user
  struct UserPosition {
    // The token that the user deposited and will be swapped in exchange for "to"
    IERC20Metadata from;
    // The token that the user will get in exchange for their "from" tokens in each swap
    IERC20Metadata to;
    // How frequently the position's swaps should be executed
    uint32 swapInterval;
    // How many swaps were executed since deposit, last modification, or last withdraw
    uint32 swapsExecuted;
    // How many "to" tokens can currently be withdrawn
    uint256 swapped;
    // How many swaps left the position has to execute
    uint32 swapsLeft;
    // How many "from" tokens there are left to swap
    uint256 remaining;
    // How many "from" tokens need to be traded in each swap
    uint120 rate;
  }

  /// @notice A list of positions that all have the same `to` token
  struct PositionSet {
    // The `to` token
    address token;
    // The position ids
    uint256[] positionIds;
  }

  /**
   * @notice Emitted when a position is terminated
   * @param user The address of the user that terminated the position
   * @param recipientUnswapped The address of the user that will receive the unswapped tokens
   * @param recipientSwapped The address of the user that will receive the swapped tokens
   * @param positionId The id of the position that was terminated
   * @param returnedUnswapped How many "from" tokens were returned to the caller
   * @param returnedSwapped How many "to" tokens were returned to the caller
   */
  event Terminated(
    address indexed user,
    address indexed recipientUnswapped,
    address indexed recipientSwapped,
    uint256 positionId,
    uint256 returnedUnswapped,
    uint256 returnedSwapped
  );

  /**
   * @notice Emitted when a position is created
   * @param depositor The address of the user that creates the position
   * @param owner The address of the user that will own the position
   * @param positionId The id of the position that was created
   * @param fromToken The address of the "from" token
   * @param toToken The address of the "to" token
   * @param swapInterval How frequently the position's swaps should be executed
   * @param rate How many "from" tokens need to be traded in each swap
   * @param startingSwap The number of the swap when the position will be executed for the first time
   * @param lastSwap The number of the swap when the position will be executed for the last time
   * @param permissions The permissions defined for the position
   */
  event Deposited(
    address indexed depositor,
    address indexed owner,
    uint256 positionId,
    address fromToken,
    address toToken,
    uint32 swapInterval,
    uint120 rate,
    uint32 startingSwap,
    uint32 lastSwap,
    IDCAPermissionManager.PermissionSet[] permissions
  );

  /**
   * @notice Emitted when a position is created and extra data is provided
   * @param positionId The id of the position that was created
   * @param data The extra data that was provided
   */
  event Miscellaneous(uint256 positionId, bytes data);

  /**
   * @notice Emitted when a user withdraws all swapped tokens from a position
   * @param withdrawer The address of the user that executed the withdraw
   * @param recipient The address of the user that will receive the withdrawn tokens
   * @param positionId The id of the position that was affected
   * @param token The address of the withdrawn tokens. It's the same as the position's "to" token
   * @param amount The amount that was withdrawn
   */
  event Withdrew(address indexed withdrawer, address indexed recipient, uint256 positionId, address token, uint256 amount);

  /**
   * @notice Emitted when a user withdraws all swapped tokens from many positions
   * @param withdrawer The address of the user that executed the withdraws
   * @param recipient The address of the user that will receive the withdrawn tokens
   * @param positions The positions to withdraw from
   * @param withdrew The total amount that was withdrawn from each token
   */
  event WithdrewMany(address indexed withdrawer, address indexed recipient, PositionSet[] positions, uint256[] withdrew);

  /**
   * @notice Emitted when a position is modified
   * @param user The address of the user that modified the position
   * @param positionId The id of the position that was modified
   * @param rate How many "from" tokens need to be traded in each swap
   * @param startingSwap The number of the swap when the position will be executed for the first time
   * @param lastSwap The number of the swap when the position will be executed for the last time
   */
  event Modified(address indexed user, uint256 positionId, uint120 rate, uint32 startingSwap, uint32 lastSwap);

  /// @notice Thrown when a user tries to create a position with the same `from` & `to`
  error InvalidToken();

  /// @notice Thrown when a user tries to create a position with a swap interval that is not allowed
  error IntervalNotAllowed();

  /// @notice Thrown when a user tries operate on a position that doesn't exist (it might have been already terminated)
  error InvalidPosition();

  /// @notice Thrown when a user tries operate on a position that they don't have access to
  error UnauthorizedCaller();

  /// @notice Thrown when a user tries to create a position with zero swaps
  error ZeroSwaps();

  /// @notice Thrown when a user tries to create a position with zero funds
  error ZeroAmount();

  /// @notice Thrown when a user tries to withdraw a position whose `to` token doesn't match the specified one
  error PositionDoesNotMatchToken();

  /// @notice Thrown when a user tries create or modify a position with an amount too big
  error AmountTooBig();

  /**
   * @notice Returns the permission manager contract
   * @return The contract itself
   */
  function permissionManager() external view returns (IDCAPermissionManager);

  /**
   * @notice Returns total created positions
   * @return The total created positions
   */
  function totalCreatedPositions() external view returns (uint256);

  /**
   * @notice Returns a user position
   * @param positionId The id of the position
   * @return position The position itself
   */
  function userPosition(uint256 positionId) external view returns (UserPosition memory position);

  /**
   * @notice Creates a new position
   * @dev Will revert:
   *      - With ZeroAddress if from, to or owner are zero
   *      - With InvalidToken if from == to
   *      - With ZeroAmount if amount is zero
   *      - With AmountTooBig if amount is too big
   *      - With ZeroSwaps if amountOfSwaps is zero
   *      - With IntervalNotAllowed if swapInterval is not allowed
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amount How many "from" tokens will be swapped in total
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param permissions Extra permissions to add to the position. Can be empty
   * @return positionId The id of the created position
   */
  function deposit(
    address from,
    address to,
    uint256 amount,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions
  ) external returns (uint256 positionId);

  /**
   * @notice Creates a new position
   * @dev Will revert:
   *      - With ZeroAddress if from, to or owner are zero
   *      - With InvalidToken if from == to
   *      - With ZeroAmount if amount is zero
   *      - With AmountTooBig if amount is too big
   *      - With ZeroSwaps if amountOfSwaps is zero
   *      - With IntervalNotAllowed if swapInterval is not allowed
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amount How many "from" tokens will be swapped in total
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param permissions Extra permissions to add to the position. Can be empty
   * @param miscellaneous Bytes that will be emitted, and associated with the position
   * @return positionId The id of the created position
   */
  function deposit(
    address from,
    address to,
    uint256 amount,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    bytes calldata miscellaneous
  ) external returns (uint256 positionId);

  /**
   * @notice Withdraws all swapped tokens from a position to a recipient
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With ZeroAddress if recipient is zero
   * @param positionId The position's id
   * @param recipient The address to withdraw swapped tokens to
   * @return swapped How much was withdrawn
   */
  function withdrawSwapped(uint256 positionId, address recipient) external returns (uint256 swapped);

  /**
   * @notice Withdraws all swapped tokens from multiple positions
   * @dev Will revert:
   *      - With InvalidPosition if any of the position ids are invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position to any of the given positions
   *      - With ZeroAddress if recipient is zero
   *      - With PositionDoesNotMatchToken if any of the positions do not match the token in their position set
   * @param positions A list positions, grouped by `to` token
   * @param recipient The address to withdraw swapped tokens to
   * @return withdrawn How much was withdrawn for each token
   */
  function withdrawSwappedMany(PositionSet[] calldata positions, address recipient) external returns (uint256[] memory withdrawn);

  /**
   * @notice Takes the unswapped balance, adds the new deposited funds and modifies the position so that
   * it is executed in newSwaps swaps
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With AmountTooBig if amount is too big
   * @param positionId The position's id
   * @param amount Amount of funds to add to the position
   * @param newSwaps The new amount of swaps
   */
  function increasePosition(
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps
  ) external;

  /**
   * @notice Withdraws the specified amount from the unswapped balance and modifies the position so that
   * it is executed in newSwaps swaps
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With ZeroSwaps if newSwaps is zero and amount is not the total unswapped balance
   * @param positionId The position's id
   * @param amount Amount of funds to withdraw from the position
   * @param newSwaps The new amount of swaps
   * @param recipient The address to send tokens to
   */
  function reducePosition(
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps,
    address recipient
  ) external;

  /**
   * @notice Terminates the position and sends all unswapped and swapped balance to the specified recipients
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With ZeroAddress if recipientUnswapped or recipientSwapped is zero
   * @param positionId The position's id
   * @param recipientUnswapped The address to withdraw unswapped tokens to
   * @param recipientSwapped The address to withdraw swapped tokens to
   * @return unswapped The unswapped balance sent to `recipientUnswapped`
   * @return swapped The swapped balance sent to `recipientSwapped`
   */
  function terminate(
    uint256 positionId,
    address recipientUnswapped,
    address recipientSwapped
  ) external returns (uint256 unswapped, uint256 swapped);
}

/**
 * @title The interface for all swap related matters
 * @notice These methods allow users to get information about the next swap, and how to execute it
 */
interface IDCAHubSwapHandler {
  /// @notice Information about a swap
  struct SwapInfo {
    // The tokens involved in the swap
    TokenInSwap[] tokens;
    // The pairs involved in the swap
    PairInSwap[] pairs;
  }

  /// @notice Information about a token's role in a swap
  struct TokenInSwap {
    // The token's address
    address token;
    // How much will be given of this token as a reward
    uint256 reward;
    // How much of this token needs to be provided by swapper
    uint256 toProvide;
    // How much of this token will be paid to the platform
    uint256 platformFee;
  }

  /// @notice Information about a pair in a swap
  struct PairInSwap {
    // The address of one of the tokens
    address tokenA;
    // The address of the other token
    address tokenB;
    // The total amount of token A swapped in this pair
    uint256 totalAmountToSwapTokenA;
    // The total amount of token B swapped in this pair
    uint256 totalAmountToSwapTokenB;
    // How much is 1 unit of token A when converted to B
    uint256 ratioAToB;
    // How much is 1 unit of token B when converted to A
    uint256 ratioBToA;
    // The swap intervals involved in the swap, represented as a byte
    bytes1 intervalsInSwap;
  }

  /// @notice A pair of tokens, represented by their indexes in an array
  struct PairIndexes {
    // The index of the token A
    uint8 indexTokenA;
    // The index of the token B
    uint8 indexTokenB;
  }

  /**
   * @notice Emitted when a swap is executed
   * @param sender The address of the user that initiated the swap
   * @param rewardRecipient The address that received the reward
   * @param callbackHandler The address that executed the callback
   * @param swapInformation All information related to the swap
   * @param borrowed How much was borrowed
   * @param fee The swap fee at the moment of the swap
   */
  event Swapped(
    address indexed sender,
    address indexed rewardRecipient,
    address indexed callbackHandler,
    SwapInfo swapInformation,
    uint256[] borrowed,
    uint32 fee
  );

  /// @notice Thrown when pairs indexes are not sorted correctly
  error InvalidPairs();

  /// @notice Thrown when trying to execute a swap, but there is nothing to swap
  error NoSwapsToExecute();

  /**
   * @notice Returns all information related to the next swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   * @param tokens The tokens involved in the next swap
   * @param pairs The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @param calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the next swap information for privileged and non-privileged accounts
   * @param oracleData Bytes to send to the oracle when executing a quote
   * @return swapInformation The information about the next swap
   */
  function getNextSwapInfo(
    address[] calldata tokens,
    PairIndexes[] calldata pairs,
    bool calculatePrivilegedAvailability,
    bytes calldata oracleData
  ) external view returns (SwapInfo memory swapInformation);

  /**
   * @notice Executes a flash swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   *      - With Paused if swaps are paused by protocol
   *      - With NoSwapsToExecute if there are no swaps to execute for the given pairs
   *      - With LiquidityNotReturned if the required tokens were not back during the callback
   * @param tokens The tokens involved in the next swap
   * @param pairsToSwap The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @param rewardRecipient The address to send the reward to
   * @param callbackHandler Address to call for callback (and send the borrowed tokens to)
   * @param borrow How much to borrow of each of the tokens in tokens. The amount must match the position of the token in the tokens array
   * @param callbackData Bytes to send to the caller during the callback
   * @param oracleData Bytes to send to the oracle when executing a quote
   * @return Information about the executed swap
   */
  function swap(
    address[] calldata tokens,
    PairIndexes[] calldata pairsToSwap,
    address rewardRecipient,
    address callbackHandler,
    uint256[] calldata borrow,
    bytes calldata callbackData,
    bytes calldata oracleData
  ) external returns (SwapInfo memory);
}

/**
 * @title The interface for handling all configuration
 * @notice This contract will manage configuration that affects all pairs, swappers, etc
 */
interface IDCAHubConfigHandler {
  /**
   * @notice Emitted when a new oracle is set
   * @param oracle The new oracle contract
   */
  event OracleSet(ITokenPriceOracle oracle);

  /**
   * @notice Emitted when a new swap fee is set
   * @param feeSet The new swap fee
   */
  event SwapFeeSet(uint32 feeSet);

  /**
   * @notice Emitted when new swap intervals are allowed
   * @param swapIntervals The new swap intervals
   */
  event SwapIntervalsAllowed(uint32[] swapIntervals);

  /**
   * @notice Emitted when some swap intervals are no longer allowed
   * @param swapIntervals The swap intervals that are no longer allowed
   */
  event SwapIntervalsForbidden(uint32[] swapIntervals);

  /**
   * @notice Emitted when a new platform fee ratio is set
   * @param platformFeeRatio The new platform fee ratio
   */
  event PlatformFeeRatioSet(uint16 platformFeeRatio);

  /**
   * @notice Emitted when allowed states of tokens are updated
   * @param tokens Array of updated tokens
   * @param allowed Array of new allow state per token were allowed[i] is the updated state of tokens[i]
   */
  event TokensAllowedUpdated(address[] tokens, bool[] allowed);

  /// @notice Thrown when trying to interact with an unallowed token
  error UnallowedToken();

  /// @notice Thrown when set allowed tokens input is not valid
  error InvalidAllowedTokensInput();

  /// @notice Thrown when trying to set a fee higher than the maximum allowed
  error HighFee();

  /// @notice Thrown when trying to set a fee that is not multiple of 100
  error InvalidFee();

  /// @notice Thrown when trying to set a fee ratio that is higher that the maximum allowed
  error HighPlatformFeeRatio();

  /**
   * @notice Returns the max fee ratio that can be set
   * @dev Cannot be modified
   * @return The maximum possible value
   */
  // solhint-disable-next-line func-name-mixedcase
  function MAX_PLATFORM_FEE_RATIO() external view returns (uint16);

  /**
   * @notice Returns the fee charged on swaps
   * @return swapFee The fee itself
   */
  function swapFee() external view returns (uint32 swapFee);

  /**
   * @notice Returns the price oracle contract
   * @return oracle The contract itself
   */
  function oracle() external view returns (ITokenPriceOracle oracle);

  /**
   * @notice Returns how much will the platform take from the fees collected in swaps
   * @return The current ratio
   */
  function platformFeeRatio() external view returns (uint16);

  /**
   * @notice Returns the max fee that can be set for swaps
   * @dev Cannot be modified
   * @return maxFee The maximum possible fee
   */
  // solhint-disable-next-line func-name-mixedcase
  function MAX_FEE() external view returns (uint32 maxFee);

  /**
   * @notice Returns a byte that represents allowed swap intervals
   * @return allowedSwapIntervals The allowed swap intervals
   */
  function allowedSwapIntervals() external view returns (bytes1 allowedSwapIntervals);

  /**
   * @notice Returns if a token is currently allowed or not
   * @return Allowed state of token
   */
  function allowedTokens(address token) external view returns (bool);

  /**
   * @notice Returns token's magnitude (10**decimals)
   * @return Stored magnitude for token
   */
  function tokenMagnitude(address token) external view returns (uint120);

  /**
   * @notice Returns whether swaps and deposits are currently paused
   * @return isPaused Whether swaps and deposits are currently paused
   */
  function paused() external view returns (bool isPaused);

  /**
   * @notice Sets a new swap fee
   * @dev Will revert with HighFee if the fee is higher than the maximum
   * @dev Will revert with InvalidFee if the fee is not multiple of 100
   * @param fee The new swap fee
   */
  function setSwapFee(uint32 fee) external;

  /**
   * @notice Sets a new price oracle
   * @dev Will revert with ZeroAddress if the zero address is passed
   * @param oracle The new oracle contract
   */
  function setOracle(ITokenPriceOracle oracle) external;

  /**
   * @notice Sets a new platform fee ratio
   * @dev Will revert with HighPlatformFeeRatio if given ratio is too high
   * @param platformFeeRatio The new ratio
   */
  function setPlatformFeeRatio(uint16 platformFeeRatio) external;

  /**
   * @notice Adds new swap intervals to the allowed list
   * @param swapIntervals The new swap intervals
   */
  function addSwapIntervalsToAllowedList(uint32[] calldata swapIntervals) external;

  /**
   * @notice Removes some swap intervals from the allowed list
   * @param swapIntervals The swap intervals to remove
   */
  function removeSwapIntervalsFromAllowedList(uint32[] calldata swapIntervals) external;

  /// @notice Pauses all swaps and deposits
  function pause() external;

  /// @notice Unpauses all swaps and deposits
  function unpause() external;
}

/**
 * @title The interface for handling platform related actions
 * @notice This contract will handle all actions that affect the platform in some way
 */
interface IDCAHubPlatformHandler {
  /**
   * @notice Emitted when someone withdraws from the paltform balance
   * @param sender The address of the user that initiated the withdraw
   * @param recipient The address that received the withdraw
   * @param amounts The tokens (and the amount) that were withdrawn
   */
  event WithdrewFromPlatform(address indexed sender, address indexed recipient, IDCAHub.AmountOfToken[] amounts);

  /**
   * @notice Withdraws tokens from the platform balance
   * @param amounts The amounts to withdraw
   * @param recipient The address that will receive the tokens
   */
  function withdrawFromPlatformBalance(IDCAHub.AmountOfToken[] calldata amounts, address recipient) external;
}

interface IDCAHub is IDCAHubParameters, IDCAHubConfigHandler, IDCAHubSwapHandler, IDCAHubPositionHandler, IDCAHubPlatformHandler {
  /// @notice Specifies an amount of a token. For example to determine how much to borrow from certain tokens
  struct AmountOfToken {
    // The tokens' address
    address token;
    // How much to borrow or withdraw of the specified token
    uint256 amount;
  }

  /// @notice Thrown when one of the parameters is a zero address
  error ZeroAddress();

  /// @notice Thrown when the expected liquidity is not returned in flash swaps
  error LiquidityNotReturned();

  /// @notice Thrown when a list of token pairs is not sorted, or if there are duplicates
  error InvalidTokens();
}

File 6 of 31 : IDCAPermissionManager.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import '@mean-finance/nft-descriptors/solidity/interfaces/IDCAHubPositionDescriptor.sol';

interface IERC721BasicEnumerable {
  /**
   * @notice Count NFTs tracked by this contract
   * @return A count of valid NFTs tracked by this contract, where each one of
   *         them has an assigned and queryable owner not equal to the zero address
   */
  function totalSupply() external view returns (uint256);
}

/**
 * @title The interface for all permission related matters
 * @notice These methods allow users to set and remove permissions to their positions
 */
interface IDCAPermissionManager is IERC721, IERC721BasicEnumerable {
  /// @notice Set of possible permissions
  enum Permission {
    INCREASE,
    REDUCE,
    WITHDRAW,
    TERMINATE
  }

  /// @notice A set of permissions for a specific operator
  struct PermissionSet {
    // The address of the operator
    address operator;
    // The permissions given to the overator
    Permission[] permissions;
  }

  /// @notice A collection of permissions sets for a specific position
  struct PositionPermissions {
    // The id of the token
    uint256 tokenId;
    // The permissions to assign to the position
    PermissionSet[] permissionSets;
  }

  /**
   * @notice Emitted when permissions for a token are modified
   * @param tokenId The id of the token
   * @param permissions The set of permissions that were updated
   */
  event Modified(uint256 tokenId, PermissionSet[] permissions);

  /**
   * @notice Emitted when the address for a new descritor is set
   * @param descriptor The new descriptor contract
   */
  event NFTDescriptorSet(IDCAHubPositionDescriptor descriptor);

  /// @notice Thrown when a user tries to set the hub, once it was already set
  error HubAlreadySet();

  /// @notice Thrown when a user provides a zero address when they shouldn't
  error ZeroAddress();

  /// @notice Thrown when a user calls a method that can only be executed by the hub
  error OnlyHubCanExecute();

  /// @notice Thrown when a user tries to modify permissions for a token they do not own
  error NotOwner();

  /// @notice Thrown when a user tries to execute a permit with an expired deadline
  error ExpiredDeadline();

  /// @notice Thrown when a user tries to execute a permit with an invalid signature
  error InvalidSignature();

  /**
   * @notice The permit typehash used in the permit signature
   * @return The typehash for the permit
   */
  // solhint-disable-next-line func-name-mixedcase
  function PERMIT_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the permission permit signature
   * @return The typehash for the permission permit
   */
  // solhint-disable-next-line func-name-mixedcase
  function PERMISSION_PERMIT_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the multi permission permit signature
   * @return The typehash for the multi permission permit
   */
  // solhint-disable-next-line func-name-mixedcase
  function MULTI_PERMISSION_PERMIT_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the permission permit signature
   * @return The typehash for the permission set
   */
  // solhint-disable-next-line func-name-mixedcase
  function PERMISSION_SET_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the multi permission permit signature
   * @return The typehash for the position permissions
   */
  // solhint-disable-next-line func-name-mixedcase
  function POSITION_PERMISSIONS_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The domain separator used in the permit signature
   * @return The domain seperator used in encoding of permit signature
   */
  // solhint-disable-next-line func-name-mixedcase
  function DOMAIN_SEPARATOR() external view returns (bytes32);

  /**
   * @notice Returns the NFT descriptor contract
   * @return The contract for the NFT descriptor
   */
  function nftDescriptor() external returns (IDCAHubPositionDescriptor);

  /**
   * @notice Returns the address of the DCA Hub
   * @return The address of the DCA Hub
   */
  function hub() external returns (address);

  /**
   * @notice Returns the next nonce to use for a given user
   * @param user The address of the user
   * @return nonce The next nonce to use
   */
  function nonces(address user) external returns (uint256 nonce);

  /**
   * @notice Returns whether the given address has the permission for the given token
   * @param id The id of the token to check
   * @param account The address of the user to check
   * @param permission The permission to check
   * @return Whether the user has the permission or not
   */
  function hasPermission(
    uint256 id,
    address account,
    Permission permission
  ) external view returns (bool);

  /**
   * @notice Returns whether the given address has the permissions for the given token
   * @param id The id of the token to check
   * @param account The address of the user to check
   * @param permissions The permissions to check
   * @return hasPermissions Whether the user has each permission or not
   */
  function hasPermissions(
    uint256 id,
    address account,
    Permission[] calldata permissions
  ) external view returns (bool[] memory hasPermissions);

  /**
   * @notice Sets the address for the hub
   * @dev Can only be successfully executed once. Once it's set, it can be modified again
   *      Will revert:
   *      - With ZeroAddress if address is zero
   *      - With HubAlreadySet if the hub has already been set
   * @param hub The address to set for the hub
   */
  function setHub(address hub) external;

  /**
   * @notice Mints a new NFT with the given id, and sets the permissions for it
   * @dev Will revert with OnlyHubCanExecute if the caller is not the hub
   * @param id The id of the new NFT
   * @param owner The owner of the new NFT
   * @param permissions Permissions to set for the new NFT
   */
  function mint(
    uint256 id,
    address owner,
    PermissionSet[] calldata permissions
  ) external;

  /**
   * @notice Burns the NFT with the given id, and clears all permissions
   * @dev Will revert with OnlyHubCanExecute if the caller is not the hub
   * @param id The token's id
   */
  function burn(uint256 id) external;

  /**
   * @notice Sets new permissions for the given position
   * @dev Will revert with NotOwner if the caller is not the token's owner.
   *      Operators that are not part of the given permission sets do not see their permissions modified.
   *      In order to remove permissions to an operator, provide an empty list of permissions for them
   * @param id The token's id
   * @param permissions A list of permission sets
   */
  function modify(uint256 id, PermissionSet[] calldata permissions) external;

  /**
   * @notice Sets new permissions for the given positions
   * @dev This is basically the same as executing multiple `modify`
   * @param permissions A list of position permissions to set
   */
  function modifyMany(PositionPermissions[] calldata permissions) external;

  /**
   * @notice Approves spending of a specific token ID by spender via signature
   * @param spender The account that is being approved
   * @param tokenId The ID of the token that is being approved for spending
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function permit(
    address spender,
    uint256 tokenId,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @notice Sets permissions via signature
   * @dev This method works similarly to `modifyMany`, but instead of being executed by the owner, it can be set by signature
   * @param permissions The permissions to set for the different positions
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function multiPermissionPermit(
    PositionPermissions[] calldata permissions,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @notice Sets permissions via signature
   * @dev This method works similarly to `modify`, but instead of being executed by the owner, it can be set my signature
   * @param permissions The permissions to set
   * @param tokenId The token's id
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function permissionPermit(
    PermissionSet[] calldata permissions,
    uint256 tokenId,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @notice Sets a new NFT descriptor
   * @dev Will revert with ZeroAddress if address is zero
   * @param descriptor The new NFT descriptor contract
   */
  function setNFTDescriptor(IDCAHubPositionDescriptor descriptor) external;
}

File 7 of 31 : Intervals.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

/// @title Intervals library
/// @notice Provides functions to easily convert from swap intervals to their byte representation and viceversa
library Intervals {
  /// @notice Thrown when a user tries convert and invalid interval to a byte representation
  error InvalidInterval();

  /// @notice Thrown when a user tries convert and invalid byte representation to an interval
  error InvalidMask();

  /// @notice Takes a swap interval and returns its byte representation
  /// @dev Will revert with InvalidInterval if the swap interval is not valid
  /// @param _swapInterval The swap interval
  /// @return The interval's byte representation
  function intervalToMask(uint32 _swapInterval) internal pure returns (bytes1) {
    if (_swapInterval == 1 minutes) return 0x01;
    if (_swapInterval == 5 minutes) return 0x02;
    if (_swapInterval == 15 minutes) return 0x04;
    if (_swapInterval == 30 minutes) return 0x08;
    if (_swapInterval == 1 hours) return 0x10;
    if (_swapInterval == 4 hours) return 0x20;
    if (_swapInterval == 1 days) return 0x40;
    if (_swapInterval == 1 weeks) return 0x80;
    revert InvalidInterval();
  }

  /// @notice Takes a byte representation of a swap interval and returns the swap interval
  /// @dev Will revert with InvalidMask if the byte representation is not valid
  /// @param _mask The byte representation
  /// @return The swap interval
  function maskToInterval(bytes1 _mask) internal pure returns (uint32) {
    if (_mask == 0x01) return 1 minutes;
    if (_mask == 0x02) return 5 minutes;
    if (_mask == 0x04) return 15 minutes;
    if (_mask == 0x08) return 30 minutes;
    if (_mask == 0x10) return 1 hours;
    if (_mask == 0x20) return 4 hours;
    if (_mask == 0x40) return 1 days;
    if (_mask == 0x80) return 1 weeks;
    revert InvalidMask();
  }

  /// @notice Takes a byte representation of a set of swap intervals and returns which ones are in the set
  /// @dev Will always return an array of length 8, with zeros at the end if there are less than 8 intervals
  /// @param _byte The byte representation
  /// @return _intervals The swap intervals in the set
  function intervalsInByte(bytes1 _byte) internal pure returns (uint32[] memory _intervals) {
    _intervals = new uint32[](8);
    uint8 _index;
    bytes1 _mask = 0x01;
    while (_byte >= _mask && _mask > 0) {
      if (_byte & _mask != 0) {
        _intervals[_index++] = maskToInterval(_mask);
      }
      _mask <<= 1;
    }
  }
}

File 8 of 31 : TokenSorting.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >0.6;

/// @title TokenSorting library
/// @notice Provides functions to sort tokens easily
library TokenSorting {
  /// @notice Takes two tokens, and returns them sorted
  /// @param _tokenA One of the tokens
  /// @param _tokenB The other token
  /// @return __tokenA The first of the tokens
  /// @return __tokenB The second of the tokens
  function sortTokens(address _tokenA, address _tokenB) internal pure returns (address __tokenA, address __tokenB) {
    (__tokenA, __tokenB) = _tokenA < _tokenB ? (_tokenA, _tokenB) : (_tokenB, _tokenA);
  }
}

File 9 of 31 : DCAHubCompanionHubProxyHandler.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.22;

import '../interfaces/IDCAHubCompanion.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

/// @dev All public functions are payable, so that they can be multicalled together with other payable functions when msg.value > 0
abstract contract DCAHubCompanionHubProxyHandler is IDCAHubCompanionHubProxyHandler {
  using SafeERC20 for IERC20;

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function permissionPermit(
    IDCAPermissionManager _permissionManager,
    IDCAPermissionManager.PermissionSet[] calldata _permissions,
    uint256 _tokenId,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external payable {
    _permissionManager.permissionPermit(_permissions, _tokenId, _deadline, _v, _r, _s);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function multiPermissionPermit(
    IDCAPermissionManager _permissionManager,
    IDCAPermissionManager.PositionPermissions[] calldata _permissions,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external payable {
    _permissionManager.multiPermissionPermit(_permissions, _deadline, _v, _r, _s);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function deposit(
    IDCAHub _hub,
    address _from,
    address _to,
    uint256 _amount,
    uint32 _amountOfSwaps,
    uint32 _swapInterval,
    address _owner,
    IDCAPermissionManager.PermissionSet[] calldata _permissions,
    bytes calldata _miscellaneous
  ) public payable virtual returns (uint256 _positionId) {
    _approveHub(address(_from), _hub, _amount);
    _positionId = _miscellaneous.length > 0
      ? _hub.deposit(_from, _to, _amount, _amountOfSwaps, _swapInterval, _owner, _permissions, _miscellaneous)
      : _hub.deposit(_from, _to, _amount, _amountOfSwaps, _swapInterval, _owner, _permissions);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function depositWithBalanceOnContract(
    IDCAHub _hub,
    address _from,
    address _to,
    uint32 _amountOfSwaps,
    uint32 _swapInterval,
    address _owner,
    IDCAPermissionManager.PermissionSet[] calldata _permissions,
    bytes calldata _miscellaneous
  ) external payable returns (uint256 _positionId) {
    uint256 _amount = IERC20(_from).balanceOf(address(this));
    return deposit(_hub, _from, _to, _amount, _amountOfSwaps, _swapInterval, _owner, _permissions, _miscellaneous);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function withdrawSwapped(
    IDCAHub _hub,
    uint256 _positionId,
    address _recipient
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.WITHDRAW) returns (uint256 _swapped) {
    _swapped = _hub.withdrawSwapped(_positionId, _recipient);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function withdrawSwappedMany(
    IDCAHub _hub,
    IDCAHub.PositionSet[] calldata _positions,
    address _recipient
  ) external payable returns (uint256[] memory _withdrawn) {
    for (uint256 i = 0; i < _positions.length; ++i) {
      uint256[] memory _positionIds = _positions[i].positionIds;
      for (uint256 j = 0; j < _positionIds.length; ++j) {
        _checkPermissionOrFail(_hub, _positionIds[j], IDCAPermissionManager.Permission.WITHDRAW);
      }
    }
    _withdrawn = _hub.withdrawSwappedMany(_positions, _recipient);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function increasePosition(
    IDCAHub _hub,
    uint256 _positionId,
    uint256 _amount,
    uint32 _newSwaps
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.INCREASE) {
    IERC20Metadata _from = _hub.userPosition(_positionId).from;
    _approveHub(address(_from), _hub, _amount);
    _hub.increasePosition(_positionId, _amount, _newSwaps);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function increasePositionWithBalanceOnContract(
    IDCAHub _hub,
    uint256 _positionId,
    uint32 _newSwaps
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.INCREASE) {
    IERC20Metadata _from = _hub.userPosition(_positionId).from;
    uint256 _amount = _from.balanceOf(address(this));
    _approveHub(address(_from), _hub, _amount);
    _hub.increasePosition(_positionId, _amount, _newSwaps);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function reducePosition(
    IDCAHub _hub,
    uint256 _positionId,
    uint256 _amount,
    uint32 _newSwaps,
    address _recipient
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.REDUCE) {
    _hub.reducePosition(_positionId, _amount, _newSwaps, _recipient);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function terminate(
    IDCAHub _hub,
    uint256 _positionId,
    address _recipientUnswapped,
    address _recipientSwapped
  )
    external
    payable
    verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.TERMINATE)
    returns (uint256 _unswapped, uint256 _swapped)
  {
    (_unswapped, _swapped) = _hub.terminate(_positionId, _recipientUnswapped, _recipientSwapped);
  }

  function _approveHub(
    address _token,
    IDCAHub _hub,
    uint256 _amount
  ) internal {
    uint256 _allowance = IERC20(_token).allowance(address(this), address(_hub));
    if (_allowance < _amount) {
      IERC20(_token).forceApprove(address(_hub), type(uint256).max);
    }
  }

  function _checkPermissionOrFail(
    IDCAHub _hub,
    uint256 _positionId,
    IDCAPermissionManager.Permission _permission
  ) internal view {
    if (!_hub.permissionManager().hasPermission(_positionId, msg.sender, _permission)) revert UnauthorizedCaller();
  }

  modifier verifyPermission(
    IDCAHub _hub,
    uint256 _positionId,
    IDCAPermissionManager.Permission _permission
  ) {
    _checkPermissionOrFail(_hub, _positionId, _permission);
    _;
  }
}

File 10 of 31 : DCAHubCompanionLibrariesHandler.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.22;

import '../libraries/InputBuilding.sol';
import '../libraries/SecondsUntilNextSwap.sol';
import '../interfaces/IDCAHubCompanion.sol';

abstract contract DCAHubCompanionLibrariesHandler is IDCAHubCompanionLibrariesHandler {
  /// @inheritdoc IDCAHubCompanionLibrariesHandler
  function getNextSwapInfo(
    IDCAHub _hub,
    Pair[] calldata _pairs,
    bool _calculatePrivilegedAvailability,
    bytes calldata _oracleData
  ) external view returns (IDCAHub.SwapInfo memory) {
    (address[] memory _tokens, IDCAHub.PairIndexes[] memory _indexes) = InputBuilding.buildGetNextSwapInfoInput(_pairs);
    return _hub.getNextSwapInfo(_tokens, _indexes, _calculatePrivilegedAvailability, _oracleData);
  }

  /// @inheritdoc IDCAHubCompanionLibrariesHandler
  function legacyGetNextSwapInfo(ILegacyDCAHub _hub, Pair[] calldata _pairs) external view returns (ILegacyDCAHub.SwapInfo memory) {
    (address[] memory _tokens, IDCAHub.PairIndexes[] memory _indexes) = InputBuilding.buildGetNextSwapInfoInput(_pairs);
    return _hub.getNextSwapInfo(_tokens, _indexes);
  }

  /// @inheritdoc IDCAHubCompanionLibrariesHandler
  function secondsUntilNextSwap(
    IDCAHub _hub,
    Pair[] calldata _pairs,
    bool _calculatePrivilegedAvailability
  ) external view returns (uint256[] memory) {
    return SecondsUntilNextSwap.secondsUntilNextSwap(_hub, _pairs, _calculatePrivilegedAvailability);
  }
}

File 11 of 31 : IDCAHubCompanion.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';
import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAPermissionManager.sol';
import './ILegacyDCAHub.sol';
import './ISharedTypes.sol';

/**
 * @notice This contract exposes many utils that are also available through libraries. The idea is to make
 *         these functions available here, so others don't need to deploy new contracts
 */
interface IDCAHubCompanionLibrariesHandler {
  /**
   * @notice Takes a list of pairs and returns how it would look like to execute a swap for all of them
   * @dev Please note that this function is very expensive. Ideally, it would be used for off-chain purposes
   * @param hub The address of the DCAHub
   * @param pairs The pairs to be involved in the swap
   * @param calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the next swap information for privileged and non-privileged accounts
   * @param oracleData Bytes to send to the oracle when executing a quote
   * @return How executing a swap for all the given pairs would look like
   */
  function getNextSwapInfo(
    IDCAHub hub,
    Pair[] calldata pairs,
    bool calculatePrivilegedAvailability,
    bytes calldata oracleData
  ) external view returns (IDCAHub.SwapInfo memory);

  /**
   * @notice Takes a list of pairs and returns how it would look like to execute a swap for all of them
   * @dev Please note that this function is very expensive. Ideally, it would be used for off-chain purposes
   * @param hub The address of the DCAHub
   * @param pairs The pairs to be involved in the swap
   * @return How executing a swap for all the given pairs would look like
   */
  function legacyGetNextSwapInfo(ILegacyDCAHub hub, Pair[] calldata pairs) external view returns (ILegacyDCAHub.SwapInfo memory);

  /**
   * @notice Returns how many seconds left until the next swap is available for a list of pairs
   * @dev Tokens in pairs may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param hub The address of the DCAHub
   * @param pairs Pairs to check
   * @param calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the seconds until next swap for privileged and non-privileged accounts
   * @return The amount of seconds until next swap for each of the pairs
   */
  function secondsUntilNextSwap(
    IDCAHub hub,
    Pair[] calldata pairs,
    bool calculatePrivilegedAvailability
  ) external view returns (uint256[] memory);
}

interface IDCAHubCompanionHubProxyHandler {
  /// @notice Thrown when a user tries operate on a position that they don't have access to
  error UnauthorizedCaller();

  /**
   * @notice Creates a new position
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amount How many "from" tokens will be swapped in total
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param miscellaneous Bytes that will be emitted, and associated with the position. If empty, no event will be emitted
   * @return positionId The id of the created position
   */
  function deposit(
    IDCAHub hub,
    address from,
    address to,
    uint256 amount,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    bytes calldata miscellaneous
  ) external payable returns (uint256 positionId);

  /**
   * @notice Creates a new position using the entire balance available on the contract
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param miscellaneous Bytes that will be emitted, and associated with the position. If empty, no event will be emitted
   * @return positionId The id of the created position
   */
  function depositWithBalanceOnContract(
    IDCAHub hub,
    address from,
    address to,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    bytes calldata miscellaneous
  ) external payable returns (uint256 positionId);

  /**
   * @notice Call the hub and withdraws all swapped tokens from a position to a recipient
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param recipient The address to withdraw swapped tokens to
   * @return swapped How much was withdrawn
   */
  function withdrawSwapped(
    IDCAHub hub,
    uint256 positionId,
    address recipient
  ) external payable returns (uint256 swapped);

  /**
   * @notice Call the hub and withdraws all swapped tokens from multiple positions
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positions A list positions, grouped by `to` token
   * @param recipient The address to withdraw swapped tokens to
   * @return withdrawn How much was withdrawn for each token
   */
  function withdrawSwappedMany(
    IDCAHub hub,
    IDCAHub.PositionSet[] calldata positions,
    address recipient
  ) external payable returns (uint256[] memory withdrawn);

  /**
   * @notice Call the hub and takes the unswapped balance, adds the new deposited funds and modifies the position so that
   * it is executed in `newSwaps` swaps
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param amount Amount of funds to add to the position
   * @param newSwaps The new amount of swaps
   */
  function increasePosition(
    IDCAHub hub,
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps
  ) external payable;

  /**
   * @notice Call the hub and takes the unswapped balance, adds the Companion's current balance and modifies the position so that
   * it is executed in `newSwaps` swaps
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param newSwaps The new amount of swaps
   */
  function increasePositionWithBalanceOnContract(
    IDCAHub hub,
    uint256 positionId,
    uint32 newSwaps
  ) external payable;

  /**
   * @notice Call the hub and withdraws the specified amount from the unswapped balance and modifies the position so that
   * it is executed in newSwaps swaps
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param amount Amount of funds to withdraw from the position
   * @param newSwaps The new amount of swaps
   * @param recipient The address to send tokens to
   */
  function reducePosition(
    IDCAHub hub,
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps,
    address recipient
  ) external payable;

  /**
   * @notice Calls the hub and terminates the position and sends all unswapped and swapped balance to the specified recipients
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param recipientUnswapped The address to withdraw unswapped tokens to
   * @param recipientSwapped The address to withdraw swapped tokens to
   * @return unswapped The unswapped balance sent to `recipientUnswapped`
   * @return swapped The swapped balance sent to `recipientSwapped`
   */
  function terminate(
    IDCAHub hub,
    uint256 positionId,
    address recipientUnswapped,
    address recipientSwapped
  ) external payable returns (uint256 unswapped, uint256 swapped);

  /**
   * @notice Calls the permission manager and sets multiple permissions via signature
   * @param permissionManager The address of the permission manager
   * @param permissions The permissions to set
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function multiPermissionPermit(
    IDCAPermissionManager permissionManager,
    IDCAPermissionManager.PositionPermissions[] calldata permissions,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external payable;

  /**
   * @notice Calls the permission manager and sets permissions via signature
   * @param permissionManager The address of the permission manager
   * @param permissions The permissions to set
   * @param tokenId The token's id
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function permissionPermit(
    IDCAPermissionManager permissionManager,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    uint256 tokenId,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external payable;
}

interface IDCAHubCompanion is IDCAHubCompanionLibrariesHandler, IDCAHubCompanionHubProxyHandler {}

File 12 of 31 : ILegacyDCAHub.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';

interface ILegacyDCAHub {
  /// @notice Information about a swap
  struct SwapInfo {
    // The tokens involved in the swap
    TokenInSwap[] tokens;
    // The pairs involved in the swap
    PairInSwap[] pairs;
  }

  /// @notice Information about a token's role in a swap
  struct TokenInSwap {
    // The token's address
    address token;
    // How much will be given of this token as a reward
    uint256 reward;
    // How much of this token needs to be provided by swapper
    uint256 toProvide;
    // How much of this token will be paid to the platform
    uint256 platformFee;
  }

  /// @notice Information about a pair in a swap
  struct PairInSwap {
    // The address of one of the tokens
    address tokenA;
    // The address of the other token
    address tokenB;
    // How much is 1 unit of token A when converted to B
    uint256 ratioAToB;
    // How much is 1 unit of token B when converted to A
    uint256 ratioBToA;
    // The swap intervals involved in the swap, represented as a byte
    bytes1 intervalsInSwap;
  }

  /**
   * @notice Returns all information related to the next swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   * @param tokens The tokens involved in the next swap
   * @param pairs The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @return swapInformation The information about the next swap
   */
  function getNextSwapInfo(address[] calldata tokens, IDCAHub.PairIndexes[] calldata pairs)
    external
    view
    returns (SwapInfo memory swapInformation);

  /**
   * @notice Executes a flash swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   *      - With Paused if swaps are paused by protocol
   *      - With NoSwapsToExecute if there are no swaps to execute for the given pairs
   *      - With LiquidityNotReturned if the required tokens were not back during the callback
   * @param tokens The tokens involved in the next swap
   * @param pairsToSwap The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @param rewardRecipient The address to send the reward to
   * @param callbackHandler Address to call for callback (and send the borrowed tokens to)
   * @param borrow How much to borrow of each of the tokens in tokens. The amount must match the position of the token in the tokens array
   * @param callbackData Bytes to send to the caller during the callback
   * @return Information about the executed swap
   */
  function swap(
    address[] calldata tokens,
    IDCAHub.PairIndexes[] calldata pairsToSwap,
    address rewardRecipient,
    address callbackHandler,
    uint256[] calldata borrow,
    bytes calldata callbackData
  ) external returns (SwapInfo memory);
}

File 13 of 31 : ISharedTypes.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7;

/// @notice A pair of tokens
struct Pair {
  address tokenA;
  address tokenB;
}

File 14 of 31 : IPermit2.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

// Minimal Permit2 interface, derived from
// https://github.com/Uniswap/permit2/blob/main/src/interfaces/ISignatureTransfer.sol
interface IPermit2 {
  struct TokenPermissions {
    address token;
    uint256 amount;
  }

  struct PermitTransferFrom {
    TokenPermissions permitted;
    uint256 nonce;
    uint256 deadline;
  }

  struct PermitBatchTransferFrom {
    TokenPermissions[] permitted;
    uint256 nonce;
    uint256 deadline;
  }

  struct SignatureTransferDetails {
    address to;
    uint256 requestedAmount;
  }

  // solhint-disable-next-line func-name-mixedcase
  function DOMAIN_SEPARATOR() external view returns (bytes32);

  function permitTransferFrom(
    PermitTransferFrom calldata permit,
    SignatureTransferDetails calldata transferDetails,
    address owner,
    bytes calldata signature
  ) external;

  function permitTransferFrom(
    PermitBatchTransferFrom memory permit,
    SignatureTransferDetails[] calldata transferDetails,
    address owner,
    bytes calldata signature
  ) external;
}

File 15 of 31 : InputBuilding.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';
import '../interfaces/ISharedTypes.sol';

/// @title Input Building Library
/// @notice Provides functions to build input for swap related actions
/// @dev Please note that these functions are very expensive. Ideally, these would be used for off-chain purposes
library InputBuilding {
  /// @notice Takes a list of pairs and returns the input necessary to check the next swap
  /// @dev Even though this function allows it, the DCAHub will fail if duplicated pairs are used
  /// @return _tokens A sorted list of all the tokens involved in the swap
  /// @return _pairsToSwap A sorted list of indexes that represent the pairs involved in the swap
  function buildGetNextSwapInfoInput(Pair[] calldata _pairs)
    internal
    pure
    returns (address[] memory _tokens, IDCAHub.PairIndexes[] memory _pairsToSwap)
  {
    (_tokens, _pairsToSwap, ) = buildSwapInput(_pairs, new IDCAHub.AmountOfToken[](0));
  }

  /// @notice Takes a list of pairs and a list of tokens to borrow and returns the input necessary to execute a swap
  /// @dev Even though this function allows it, the DCAHub will fail if duplicated pairs are used
  /// @return _tokens A sorted list of all the tokens involved in the swap
  /// @return _pairsToSwap A sorted list of indexes that represent the pairs involved in the swap
  /// @return _borrow A list of amounts to borrow, based on the sorted token list
  function buildSwapInput(Pair[] calldata _pairs, IDCAHub.AmountOfToken[] memory _toBorrow)
    internal
    pure
    returns (
      address[] memory _tokens,
      IDCAHub.PairIndexes[] memory _pairsToSwap,
      uint256[] memory _borrow
    )
  {
    _tokens = _calculateUniqueTokens(_pairs, _toBorrow);
    _pairsToSwap = _calculatePairIndexes(_pairs, _tokens);
    _borrow = _calculateTokensToBorrow(_toBorrow, _tokens);
  }

  /// @dev Given a list of token pairs and tokens to borrow, returns a list of all the tokens involved, sorted
  function _calculateUniqueTokens(Pair[] memory _pairs, IDCAHub.AmountOfToken[] memory _toBorrow)
    private
    pure
    returns (address[] memory _tokens)
  {
    uint256 _uniqueTokens;
    address[] memory _tokensPlaceholder = new address[](_pairs.length * 2 + _toBorrow.length);

    // Load tokens in pairs onto placeholder
    for (uint256 i; i < _pairs.length; i++) {
      bool _foundA = false;
      bool _foundB = false;
      for (uint256 j; j < _uniqueTokens && !(_foundA && _foundB); j++) {
        if (!_foundA && _tokensPlaceholder[j] == _pairs[i].tokenA) _foundA = true;
        if (!_foundB && _tokensPlaceholder[j] == _pairs[i].tokenB) _foundB = true;
      }

      if (!_foundA) _tokensPlaceholder[_uniqueTokens++] = _pairs[i].tokenA;
      if (!_foundB) _tokensPlaceholder[_uniqueTokens++] = _pairs[i].tokenB;
    }

    // Load tokens to borrow onto placeholder
    for (uint256 i; i < _toBorrow.length; i++) {
      bool _found = false;
      for (uint256 j; j < _uniqueTokens && !_found; j++) {
        if (_tokensPlaceholder[j] == _toBorrow[i].token) _found = true;
      }
      if (!_found) _tokensPlaceholder[_uniqueTokens++] = _toBorrow[i].token;
    }

    // Load sorted into new array
    _tokens = new address[](_uniqueTokens);
    for (uint256 i; i < _uniqueTokens; i++) {
      address _token = _tokensPlaceholder[i];

      // Find index where the token should be
      uint256 _tokenIndex;
      while (_tokens[_tokenIndex] < _token && _tokens[_tokenIndex] != address(0)) _tokenIndex++;

      // Move everything one place back
      for (uint256 j = i; j > _tokenIndex; j--) {
        _tokens[j] = _tokens[j - 1];
      }

      // Set token on the correct index
      _tokens[_tokenIndex] = _token;
    }
  }

  /// @dev Given a list of pairs, and a list of sorted tokens, it translates the first list into indexes of the second list. This list of indexes will
  /// be sorted. For example, if pairs are [{ tokenA, tokenB }, { tokenC, tokenB }] and tokens are: [ tokenA, tokenB, tokenC ], the following is returned
  /// [ { 0, 1 }, { 1, 1 }, { 1, 2 } ]
  function _calculatePairIndexes(Pair[] calldata _pairs, address[] memory _tokens)
    private
    pure
    returns (IDCAHub.PairIndexes[] memory _pairIndexes)
  {
    _pairIndexes = new IDCAHub.PairIndexes[](_pairs.length);
    uint256 _count;

    for (uint8 i; i < _tokens.length; i++) {
      for (uint8 j = i + 1; j < _tokens.length; j++) {
        for (uint256 k; k < _pairs.length; k++) {
          if (
            (_tokens[i] == _pairs[k].tokenA && _tokens[j] == _pairs[k].tokenB) ||
            (_tokens[i] == _pairs[k].tokenB && _tokens[j] == _pairs[k].tokenA)
          ) {
            _pairIndexes[_count++] = IDCAHubSwapHandler.PairIndexes({indexTokenA: i, indexTokenB: j});
          }
        }
      }
    }
  }

  /// @dev Given a list of tokens to borrow and a list of sorted tokens, it translated the first list into a list of amounts, sorted by the indexed of
  /// the seconds list. For example, if `toBorrow` are [{ tokenA, 100 }, { tokenC, 200 }, { tokenB, 500 }] and tokens are [ tokenA, tokenB, tokenC], the
  /// following is returned [100, 500, 200]
  function _calculateTokensToBorrow(IDCAHub.AmountOfToken[] memory _toBorrow, address[] memory _tokens)
    private
    pure
    returns (uint256[] memory _borrow)
  {
    _borrow = new uint256[](_tokens.length);

    for (uint256 i; i < _toBorrow.length; i++) {
      uint256 j;
      while (_tokens[j] != _toBorrow[i].token) j++;
      _borrow[j] = _toBorrow[i].amount;
    }
  }
}

File 16 of 31 : Permit2Transfers.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.22;

import {IPermit2} from '../interfaces/external/IPermit2.sol';

/**
 * @title Permit2 Transfers Library
 * @author Sam Bugs
 * @notice A small library to call Permit2's transfer from methods
 */
library Permit2Transfers {
  /**
   * @notice Executes a transfer from using Permit2
   * @param _permit2 The Permit2 contract
   * @param _token The token to transfer
   * @param _amount The amount to transfer
   * @param _nonce The owner's nonce
   * @param _deadline The signature's expiration deadline
   * @param _signature The signature that allows the transfer
   * @param _recipient The address that will receive the funds
   */
  function takeFromCaller(
    IPermit2 _permit2,
    address _token,
    uint256 _amount,
    uint256 _nonce,
    uint256 _deadline,
    bytes calldata _signature,
    address _recipient
  ) internal {
    _permit2.permitTransferFrom(
      // The permit message.
      IPermit2.PermitTransferFrom({permitted: IPermit2.TokenPermissions({token: _token, amount: _amount}), nonce: _nonce, deadline: _deadline}),
      // The transfer recipient and amount.
      IPermit2.SignatureTransferDetails({to: _recipient, requestedAmount: _amount}),
      // The owner of the tokens, which must also be
      // the signer of the message, otherwise this call
      // will fail.
      msg.sender,
      // The packed signature that was the result of signing
      // the EIP712 hash of `permit`.
      _signature
    );
  }

  /**
   * @notice Executes a batch transfer from using Permit2
   * @param _permit2 The Permit2 contract
   * @param _tokens The amount of tokens to transfer
   * @param _nonce The owner's nonce
   * @param _deadline The signature's expiration deadline
   * @param _signature The signature that allows the transfer
   * @param _recipient The address that will receive the funds
   */
  function batchTakeFromCaller(
    IPermit2 _permit2,
    IPermit2.TokenPermissions[] calldata _tokens,
    uint256 _nonce,
    uint256 _deadline,
    bytes calldata _signature,
    address _recipient
  ) internal {
    if (_tokens.length > 0) {
      _permit2.permitTransferFrom(
        // The permit message.
        IPermit2.PermitBatchTransferFrom({permitted: _tokens, nonce: _nonce, deadline: _deadline}),
        // The transfer recipients and amounts.
        _buildTransferDetails(_tokens, _recipient),
        // The owner of the tokens, which must also be
        // the signer of the message, otherwise this call
        // will fail.
        msg.sender,
        // The packed signature that was the result of signing
        // the EIP712 hash of `permit`.
        _signature
      );
    }
  }

  function _buildTransferDetails(IPermit2.TokenPermissions[] calldata _tokens, address _recipient)
    private
    pure
    returns (IPermit2.SignatureTransferDetails[] memory _details)
  {
    _details = new IPermit2.SignatureTransferDetails[](_tokens.length);
    for (uint256 i; i < _details.length; ++i) {
      _details[i] = IPermit2.SignatureTransferDetails({to: _recipient, requestedAmount: _tokens[i].amount});
    }
  }
}

File 17 of 31 : SecondsUntilNextSwap.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';
import '@mean-finance/dca-v2-core/contracts/libraries/TokenSorting.sol';
import '@mean-finance/dca-v2-core/contracts/libraries/Intervals.sol';
import '../interfaces/ISharedTypes.sol';

/**
 * @title Seconds Until Next Swap Library
 * @notice Provides functions to calculate how long users have to wait until a pair's next swap is available
 */
library SecondsUntilNextSwap {
  /**
   * @notice Returns how many seconds left until the next swap is available for a specific pair
   * @dev _tokenA and _tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param _hub The address of the DCA Hub
   * @param _tokenA One of the pair's tokens
   * @param _tokenB The other of the pair's tokens
   * @param _calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the seconds until next swap for privileged and non-privileged accounts
   * @return The amount of seconds until next swap. Returns 0 if a swap can already be executed and max(uint256) if there is nothing to swap
   */
  function secondsUntilNextSwap(
    IDCAHub _hub,
    address _tokenA,
    address _tokenB,
    bool _calculatePrivilegedAvailability
  ) internal view returns (uint256) {
    (address __tokenA, address __tokenB) = TokenSorting.sortTokens(_tokenA, _tokenB);
    bytes1 _activeIntervals = _hub.activeSwapIntervals(__tokenA, __tokenB);
    bytes1 _mask = 0x01;
    uint256 _smallerIntervalBlocking;
    while (_activeIntervals >= _mask && _mask > 0) {
      if (_activeIntervals & _mask == _mask) {
        (, uint224 _nextAmountToSwapAToB, uint32 _lastSwappedAt, uint224 _nextAmountToSwapBToA) = _hub.swapData(_tokenA, _tokenB, _mask);
        uint32 _swapInterval = Intervals.maskToInterval(_mask);
        uint256 _nextAvailable = ((_lastSwappedAt / _swapInterval) + 1) * _swapInterval;
        if (!_calculatePrivilegedAvailability) {
          // If the caller does not have privileges, then they will have to wait a little more to execute swaps
          _nextAvailable += _swapInterval / 3;
        }
        if (_nextAmountToSwapAToB > 0 || _nextAmountToSwapBToA > 0) {
          if (_nextAvailable <= block.timestamp) {
            return _smallerIntervalBlocking;
          } else {
            return _nextAvailable - block.timestamp;
          }
        } else if (_nextAvailable > block.timestamp) {
          _smallerIntervalBlocking = _smallerIntervalBlocking == 0 ? _nextAvailable - block.timestamp : _smallerIntervalBlocking;
        }
      }
      _mask <<= 1;
    }
    return type(uint256).max;
  }

  /**
   * @notice Returns how many seconds left until the next swap is available for a list of pairs
   * @dev Tokens in pairs may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param _hub The address of the DCA Hub
   * @param _pairs Pairs to check
   * @return _seconds The amount of seconds until next swap for each of the pairs
   * @param _calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the seconds until next swap for privileged and non-privileged accounts
   */
  function secondsUntilNextSwap(
    IDCAHub _hub,
    Pair[] calldata _pairs,
    bool _calculatePrivilegedAvailability
  ) internal view returns (uint256[] memory _seconds) {
    _seconds = new uint256[](_pairs.length);
    for (uint256 i; i < _pairs.length; i++) {
      _seconds[i] = secondsUntilNextSwap(_hub, _pairs[i].tokenA, _pairs[i].tokenB, _calculatePrivilegedAvailability);
    }
  }
}

File 18 of 31 : BaseCompanion.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.22;

import './SwapAdapter.sol';
import './PayableMulticall.sol';
import {SimulationAdapter} from '@mean-finance/call-simulation/contracts/SimulationAdapter.sol';
import {IPermit2} from '../interfaces/external/IPermit2.sol';
import {Permit2Transfers} from '../libraries/Permit2Transfers.sol';
import './Governable.sol';

/**
 * @notice This contract will work as base companion for all our contracts. It will extend the capabilities of our companion
 *         contracts so that they can execute multicalls, swaps, revokes and more
 * @dev All public functions are payable, so that they can be multicalled together with other payable functions when msg.value > 0
 */
abstract contract BaseCompanion is SimulationAdapter, Governable, SwapAdapter, PayableMulticall {
  using Permit2Transfers for IPermit2;
  using SafeERC20 for IERC20;

  /**
   * @notice Returns the address of the Permit2 contract
   * @dev This value is constant and cannot change
   * @return The address of the Permit2 contract
   */
  // solhint-disable-next-line var-name-mixedcase
  IPermit2 public immutable PERMIT2;

  /// @notice The address of the swapper
  address public swapper;

  /// @notice The address of the allowance target
  address public allowanceTarget;

  constructor(
    address _swapper,
    address _allowanceTarget,
    address _governor,
    IPermit2 _permit2
  ) SwapAdapter() Governable(_governor) {
    swapper = _swapper;
    allowanceTarget = _allowanceTarget;
    PERMIT2 = _permit2;
  }

  receive() external payable {}

  /**
   * @notice Sends the specified amount of the given token to the recipient
   * @param _token The token to transfer
   * @param _amount The amount to transfer
   * @param _recipient The recipient of the token balance
   */
  function sendToRecipient(
    address _token,
    uint256 _amount,
    address _recipient
  ) external payable {
    _sendToRecipient(_token, _amount, _recipient);
  }

  /**
   * @notice Takes the given amount of tokens from the caller and transfers it to this contract
   * @param _token The token to take
   * @param _amount The amount to take
   */
  function takeFromCaller(
    IERC20 _token,
    uint256 _amount,
    address _recipient
  ) external payable {
    _token.safeTransferFrom(msg.sender, _recipient, _amount);
  }

  /**
   * @notice Executes a swap against the swapper
   * @param _allowanceToken The token to set allowance for (can be set to zero address to ignore)
   * @param _value The value to send to the swapper as part of the swap
   * @param _swapData The swap data
   * @param _tokenOut The token that will be bought as part of the swap
   */
  function runSwap(
    address _allowanceToken,
    uint256 _value,
    bytes calldata _swapData,
    address _tokenOut
  ) external payable returns (uint256 _amountOut) {
    if (_allowanceToken != address(0)) {
      IERC20(_allowanceToken).forceApprove(allowanceTarget, type(uint256).max);
    }

    _executeSwap(swapper, _swapData, _value);

    _amountOut = _tokenOut == PROTOCOL_TOKEN ? address(this).balance : IERC20(_tokenOut).balanceOf(address(this));
  }

  /**
   * @notice Takes the given amount of tokens from the caller with Permit2 and transfers it to this contract
   * @param _token The token to take
   * @param _amount The amount to take
   * @param _nonce The signed nonce
   * @param _deadline The signature's deadline
   * @param _signature The owner's signature
   * @param _recipient The address that will receive the funds
   */
  function permitTakeFromCaller(
    address _token,
    uint256 _amount,
    uint256 _nonce,
    uint256 _deadline,
    bytes calldata _signature,
    address _recipient
  ) external payable {
    PERMIT2.takeFromCaller(_token, _amount, _nonce, _deadline, _signature, _recipient);
  }

  /**
   * @notice Takes the a batch of tokens from the caller with Permit2 and transfers it to this contract
   * @param _tokens The tokens to take
   * @param _nonce The signed nonce
   * @param _deadline The signature's deadline
   * @param _signature The owner's signature
   * @param _recipient The address that will receive the funds
   */
  function batchPermitTakeFromCaller(
    IPermit2.TokenPermissions[] calldata _tokens,
    uint256 _nonce,
    uint256 _deadline,
    bytes calldata _signature,
    address _recipient
  ) external payable {
    PERMIT2.batchTakeFromCaller(_tokens, _nonce, _deadline, _signature, _recipient);
  }

  /**
   * @notice Checks if the contract has any balance of the given token, and if it does,
   *         it sends it to the given recipient
   * @param _token The token to check
   * @param _recipient The recipient of the token balance
   */
  function sendBalanceOnContractToRecipient(address _token, address _recipient) external payable {
    _sendBalanceOnContractToRecipient(_token, _recipient);
  }

  /**
   * @notice Sets a new swapper and allowance target
   * @param _newSwapper The address of the new swapper
   * @param _newAllowanceTarget The address of the new allowance target
   */
  function setSwapper(address _newSwapper, address _newAllowanceTarget) external onlyGovernor {
    swapper = _newSwapper;
    allowanceTarget = _newAllowanceTarget;
  }
}

File 19 of 31 : Governable.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import './interfaces/IGovernable.sol';

/**
 * @notice This contract is meant to be used in other contracts. By using this contract,
 *         a specific address will be given a "governor" role, which basically will be able to
 *         control certains aspects of the contract. There are other contracts that do the same,
 *         but this contract forces a new governor to accept the role before it's transferred.
 *         This is a basically a safety measure to prevent losing access to the contract.
 */
abstract contract Governable is IGovernable {
  /// @inheritdoc IGovernable
  address public governor;

  /// @inheritdoc IGovernable
  address public pendingGovernor;

  constructor(address _governor) {
    if (_governor == address(0)) revert GovernorIsZeroAddress();
    governor = _governor;
  }

  /// @inheritdoc IGovernable
  function isGovernor(address _account) public view returns (bool) {
    return _account == governor;
  }

  /// @inheritdoc IGovernable
  function isPendingGovernor(address _account) public view returns (bool) {
    return _account == pendingGovernor;
  }

  /// @inheritdoc IGovernable
  function setPendingGovernor(address _pendingGovernor) external onlyGovernor {
    pendingGovernor = _pendingGovernor;
    emit PendingGovernorSet(_pendingGovernor);
  }

  /// @inheritdoc IGovernable
  function acceptPendingGovernor() external onlyPendingGovernor {
    governor = pendingGovernor;
    pendingGovernor = address(0);
    emit PendingGovernorAccepted();
  }

  modifier onlyGovernor() {
    if (!isGovernor(msg.sender)) revert OnlyGovernor();
    _;
  }

  modifier onlyPendingGovernor() {
    if (!isPendingGovernor(msg.sender)) revert OnlyPendingGovernor();
    _;
  }
}

File 20 of 31 : PayableMulticall.sol
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/utils/Address.sol';

/**
 * @dev Adding this contract will enable batching calls. This is basically the same as Open Zeppelin's
 *      Multicall contract, but we have made it payable. It supports both payable and non payable
 *      functions. However, if `msg.value` is not zero, then non payable functions cannot be called.
 *      Any contract that uses this Multicall version should be very careful when using msg.value.
 *      For more context, read: https://github.com/Uniswap/v3-periphery/issues/52
 */
abstract contract PayableMulticall {
  /**
   * @notice Receives and executes a batch of function calls on this contract.
   * @param _data A list of different function calls to execute
   * @return _results The result of executing each of those calls
   */
  function multicall(bytes[] calldata _data) external payable returns (bytes[] memory _results) {
    _results = new bytes[](_data.length);
    for (uint256 i = 0; i < _data.length; ) {
      _results[i] = Address.functionDelegateCall(address(this), _data[i]);
      unchecked {
        i++;
      }
    }
    return _results;
  }
}

File 21 of 31 : SwapAdapter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/Address.sol';

abstract contract SwapAdapter {
  using SafeERC20 for IERC20;
  using Address for address;
  using Address for address payable;

  /// @notice Describes how the allowance should be revoked for the given spender
  struct RevokeAction {
    address spender;
    IERC20[] tokens;
  }

  address public constant PROTOCOL_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

  /**
   * @notice Takes the given amount of tokens from the caller
   * @param _token The token to check
   * @param _amount The amount to take
   */
  function _takeFromMsgSender(IERC20 _token, uint256 _amount) internal virtual {
    _token.safeTransferFrom(msg.sender, address(this), _amount);
  }

  /**
   * @notice Executes a swap for the given swapper
   * @param _swapper The actual swapper
   * @param _swapData The swap execution data
   */
  function _executeSwap(
    address _swapper,
    bytes calldata _swapData,
    uint256 _value
  ) internal virtual {
    _swapper.functionCallWithValue(_swapData, _value);
  }

  /**
   * @notice Transfers the given amount of tokens from the contract to the recipient
   * @param _token The token to check
   * @param _amount The amount to send
   * @param _recipient The recipient
   */
  function _sendToRecipient(
    address _token,
    uint256 _amount,
    address _recipient
  ) internal virtual {
    if (_recipient == address(0)) _recipient = msg.sender;
    if (_token == PROTOCOL_TOKEN) {
      payable(_recipient).sendValue(_amount);
    } else {
      IERC20(_token).safeTransfer(_recipient, _amount);
    }
  }

  /**
   * @notice Checks if the contract has any balance of the given token, and if it does,
   *         it sends it to the given recipient
   * @param _token The token to check
   * @param _recipient The recipient of the token balance
   */
  function _sendBalanceOnContractToRecipient(address _token, address _recipient) internal virtual {
    uint256 _balance = _token == PROTOCOL_TOKEN ? address(this).balance : IERC20(_token).balanceOf(address(this));
    if (_balance > 0) {
      _sendToRecipient(_token, _balance, _recipient);
    }
  }

  /**
   * @notice Revokes ERC20 allowances for the given spenders
   * @dev If exposed, then it should be permissioned
   * @param _revokeActions The spenders and tokens to revoke
   */
  function _revokeAllowances(RevokeAction[] calldata _revokeActions) internal virtual {
    for (uint256 i = 0; i < _revokeActions.length; ) {
      RevokeAction memory _action = _revokeActions[i];
      for (uint256 j = 0; j < _action.tokens.length; ) {
        _action.tokens[j].forceApprove(_action.spender, 0);
        unchecked {
          j++;
        }
      }
      unchecked {
        i++;
      }
    }
  }
}

File 22 of 31 : IGovernable.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

/**
 * @title A contract that manages a "governor" role
 */
interface IGovernable {
  /// @notice Thrown when trying to set the zero address as governor
  error GovernorIsZeroAddress();

  /// @notice Thrown when trying to execute an action that only the governor an execute
  error OnlyGovernor();

  /// @notice Thrown when trying to execute an action that only the pending governor an execute
  error OnlyPendingGovernor();

  /**
   * @notice Emitted when a new pending governor is set
   * @param newPendingGovernor The new pending governor
   */
  event PendingGovernorSet(address newPendingGovernor);

  /**
   * @notice Emitted when the pending governor accepts the role and becomes the governor
   */
  event PendingGovernorAccepted();

  /**
   * @notice Returns the address of the governor
   * @return The address of the governor
   */
  function governor() external view returns (address);

  /**
   * @notice Returns the address of the pending governor
   * @return The address of the pending governor
   */
  function pendingGovernor() external view returns (address);

  /**
   * @notice Returns whether the given account is the current governor
   * @param account The account to check
   * @return Whether it is the current governor or not
   */
  function isGovernor(address account) external view returns (bool);

  /**
   * @notice Returns whether the given account is the pending governor
   * @param account The account to check
   * @return Whether it is the pending governor or not
   */
  function isPendingGovernor(address account) external view returns (bool);

  /**
   * @notice Sets a new pending governor
   * @dev Only the current governor can execute this action
   * @param pendingGovernor The new pending governor
   */
  function setPendingGovernor(address pendingGovernor) external;

  /**
   * @notice Sets the pending governor as the governor
   * @dev Only the pending governor can execute this action
   */
  function acceptPendingGovernor() external;
}

File 23 of 31 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}

File 24 of 31 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
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 25 of 31 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
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].
     *
     * CAUTION: See Security Considerations above.
     */
    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 26 of 31 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../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 An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, 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);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @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.encodeCall(token.approve, (spender, value));

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

    /**
     * @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);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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(token).code.length > 0;
    }
}

File 27 of 31 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 28 of 31 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) 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 FailedInnerCall();
        }
    }
}

File 29 of 31 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 30 of 31 : IDCAHubPositionDescriptor.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

/**
 * @title The interface for generating a description for a position in a DCA Hub
 * @notice Contracts that implement this interface must return a base64 JSON with the entire description
 */
interface IDCAHubPositionDescriptor {
  /**
   * @notice Generates a positions's description, both the JSON and the image inside
   * @param hub The address of the DCA Hub
   * @param positionId The token/position id
   * @return description The position's description
   */
  function tokenURI(address hub, uint256 positionId) external view returns (string memory description);
}

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

/**
 * @title The interface for an oracle that provides price quotes
 * @notice These methods allow users to add support for pairs, and then ask for quotes
 */
interface ITokenPriceOracle {
  /// @notice Thrown when trying to add support for a pair that cannot be supported
  error PairCannotBeSupported(address tokenA, address tokenB);

  /// @notice Thrown when trying to execute a quote with a pair that isn't supported yet
  error PairNotSupportedYet(address tokenA, address tokenB);

  /**
   * @notice Returns whether this oracle can support the given pair of tokens
   * @dev tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @return Whether the given pair of tokens can be supported by the oracle
   */
  function canSupportPair(address tokenA, address tokenB) external view returns (bool);

  /**
   * @notice Returns whether this oracle is already supporting the given pair of tokens
   * @dev tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @return Whether the given pair of tokens is already being supported by the oracle
   */
  function isPairAlreadySupported(address tokenA, address tokenB) external view returns (bool);

  /**
   * @notice Returns a quote, based on the given tokens and amount
   * @dev Will revert if pair isn't supported
   * @param tokenIn The token that will be provided
   * @param amountIn The amount that will be provided
   * @param tokenOut The token we would like to quote
   * @param data Custom data that the oracle might need to operate
   * @return amountOut How much `tokenOut` will be returned in exchange for `amountIn` amount of `tokenIn`
   */
  function quote(
    address tokenIn,
    uint256 amountIn,
    address tokenOut,
    bytes calldata data
  ) external view returns (uint256 amountOut);

  /**
   * @notice Add or reconfigures the support for a given pair. This function will let the oracle take some actions
   *         to configure the pair, in preparation for future quotes. Can be called many times in order to let the oracle
   *         re-configure for a new context
   * @dev Will revert if pair cannot be supported. tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @param data Custom data that the oracle might need to operate
   */
  function addOrModifySupportForPair(
    address tokenA,
    address tokenB,
    bytes calldata data
  ) external;

  /**
   * @notice Adds support for a given pair if the oracle didn't support it already. If called for a pair that is already supported,
   *         then nothing will happen. This function will let the oracle take some actions to configure the pair, in preparation
   *         for future quotes
   * @dev Will revert if pair cannot be supported. tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @param data Custom data that the oracle might need to operate
   */
  function addSupportForPairIfNeeded(
    address tokenA,
    address tokenB,
    bytes calldata data
  ) external;
}

Settings
{
  "viaIR": false,
  "optimizer": {
    "runs": 9999,
    "enabled": true
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {},
  "remappings": [
    "@openzeppelin/contracts/=node_modules/@mean-finance/dca-v2-periphery/node_modules/@openzeppelin/contracts/",
    "@mean-finance/oracles/=node_modules/@mean-finance/oracles/",
    "@mean-finance/nft-descriptors/=node_modules/@mean-finance/nft-descriptors/",
    "@mean-finance/call-simulation/contracts/=node_modules/@mean-finance/call-simulation/src/",
    "@mean-finance/swappers/=node_modules/@mean-finance/swappers/",
    "@mean-finance/dca-v2-core/=node_modules/@mean-finance/dca-v2-core/",
    "@mean-finance/permit2-adapter/=node_modules/@mean-finance/permit2-adapter/src/",
    "@mean-finance/transformers/=node_modules/@mean-finance/transformers/solidity/contracts/transformers/",
    "@mean-finance/dca-v2-periphery/=node_modules/@mean-finance/dca-v2-periphery/",
    "@mean-finance/uniswap-v3-oracle/=node_modules/@mean-finance/uniswap-v3-oracle/",
    "@call-simulation/=node_modules/@mean-finance/call-simulation/src/",
    "@chainlink/=node_modules/@chainlink/",
    "@api3/=node_modules/@api3/",
    "@uniswap/=node_modules/@uniswap/",
    "keep3r-v2/=node_modules/keep3r-v2/",
    "base64-sol/=node_modules/base64-sol/",
    "@sphinx-labs/contracts/=lib/sphinx/packages/contracts/contracts/foundry/",
    "forge-std/=lib/forge-std/src/",
    "@rari-capital/solmate/=lib/solmate/",
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "solmate/=lib/solmate/src/",
    "sphinx/=lib/sphinx/packages/contracts/contracts/forge-std/src/"
  ]
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_swapper","type":"address"},{"internalType":"address","name":"_allowanceTarget","type":"address"},{"internalType":"address","name":"_governor","type":"address"},{"internalType":"contract IPermit2","name":"_permit2","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"GovernorIsZeroAddress","type":"error"},{"inputs":[],"name":"InvalidMask","type":"error"},{"inputs":[],"name":"OnlyGovernor","type":"error"},{"inputs":[],"name":"OnlyPendingGovernor","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"result","type":"bytes"},{"internalType":"uint256","name":"gasSpent","type":"uint256"}],"internalType":"struct ISimulationAdapter.SimulationResult","name":"result","type":"tuple"}],"name":"SimulatedCall","type":"error"},{"inputs":[],"name":"UnauthorizedCaller","type":"error"},{"anonymous":false,"inputs":[],"name":"PendingGovernorAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newPendingGovernor","type":"address"}],"name":"PendingGovernorSet","type":"event"},{"inputs":[],"name":"PERMIT2","outputs":[{"internalType":"contract IPermit2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROTOCOL_TOKEN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptPendingGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"allowanceTarget","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct IPermit2.TokenPermissions[]","name":"_tokens","type":"tuple[]"},{"internalType":"uint256","name":"_nonce","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"batchPermitTakeFromCaller","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_amountOfSwaps","type":"uint32"},{"internalType":"uint32","name":"_swapInterval","type":"uint32"},{"internalType":"address","name":"_owner","type":"address"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"_permissions","type":"tuple[]"},{"internalType":"bytes","name":"_miscellaneous","type":"bytes"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint32","name":"_amountOfSwaps","type":"uint32"},{"internalType":"uint32","name":"_swapInterval","type":"uint32"},{"internalType":"address","name":"_owner","type":"address"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"_permissions","type":"tuple[]"},{"internalType":"bytes","name":"_miscellaneous","type":"bytes"}],"name":"depositWithBalanceOnContract","outputs":[{"internalType":"uint256","name":"_positionId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"internalType":"struct Pair[]","name":"_pairs","type":"tuple[]"},{"internalType":"bool","name":"_calculatePrivilegedAvailability","type":"bool"},{"internalType":"bytes","name":"_oracleData","type":"bytes"}],"name":"getNextSwapInfo","outputs":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"toProvide","type":"uint256"},{"internalType":"uint256","name":"platformFee","type":"uint256"}],"internalType":"struct IDCAHubSwapHandler.TokenInSwap[]","name":"tokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"totalAmountToSwapTokenA","type":"uint256"},{"internalType":"uint256","name":"totalAmountToSwapTokenB","type":"uint256"},{"internalType":"uint256","name":"ratioAToB","type":"uint256"},{"internalType":"uint256","name":"ratioBToA","type":"uint256"},{"internalType":"bytes1","name":"intervalsInSwap","type":"bytes1"}],"internalType":"struct IDCAHubSwapHandler.PairInSwap[]","name":"pairs","type":"tuple[]"}],"internalType":"struct IDCAHubSwapHandler.SwapInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_newSwaps","type":"uint32"}],"name":"increasePosition","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"uint32","name":"_newSwaps","type":"uint32"}],"name":"increasePositionWithBalanceOnContract","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isGovernor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isPendingGovernor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILegacyDCAHub","name":"_hub","type":"address"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"internalType":"struct Pair[]","name":"_pairs","type":"tuple[]"}],"name":"legacyGetNextSwapInfo","outputs":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"toProvide","type":"uint256"},{"internalType":"uint256","name":"platformFee","type":"uint256"}],"internalType":"struct ILegacyDCAHub.TokenInSwap[]","name":"tokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"ratioAToB","type":"uint256"},{"internalType":"uint256","name":"ratioBToA","type":"uint256"},{"internalType":"bytes1","name":"intervalsInSwap","type":"bytes1"}],"internalType":"struct ILegacyDCAHub.PairInSwap[]","name":"pairs","type":"tuple[]"}],"internalType":"struct ILegacyDCAHub.SwapInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDCAPermissionManager","name":"_permissionManager","type":"address"},{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"permissionSets","type":"tuple[]"}],"internalType":"struct IDCAPermissionManager.PositionPermissions[]","name":"_permissions","type":"tuple[]"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"multiPermissionPermit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"_data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"_results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"pendingGovernor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDCAPermissionManager","name":"_permissionManager","type":"address"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"enum IDCAPermissionManager.Permission[]","name":"permissions","type":"uint8[]"}],"internalType":"struct IDCAPermissionManager.PermissionSet[]","name":"_permissions","type":"tuple[]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"permissionPermit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_nonce","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"permitTakeFromCaller","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_newSwaps","type":"uint32"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"reducePosition","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_allowanceToken","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_swapData","type":"bytes"},{"internalType":"address","name":"_tokenOut","type":"address"}],"name":"runSwap","outputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"internalType":"struct Pair[]","name":"_pairs","type":"tuple[]"},{"internalType":"bool","name":"_calculatePrivilegedAvailability","type":"bool"}],"name":"secondsUntilNextSwap","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"sendBalanceOnContractToRecipient","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"sendToRecipient","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_pendingGovernor","type":"address"}],"name":"setPendingGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newSwapper","type":"address"},{"internalType":"address","name":"_newAllowanceTarget","type":"address"}],"name":"setSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"_calls","type":"bytes[]"}],"name":"simulate","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"result","type":"bytes"},{"internalType":"uint256","name":"gasSpent","type":"uint256"}],"internalType":"struct ISimulationAdapter.SimulationResult[]","name":"_results","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_call","type":"bytes"}],"name":"simulateAndRevert","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"takeFromCaller","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"address","name":"_recipientUnswapped","type":"address"},{"internalType":"address","name":"_recipientSwapped","type":"address"}],"name":"terminate","outputs":[{"internalType":"uint256","name":"_unswapped","type":"uint256"},{"internalType":"uint256","name":"_swapped","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"internalType":"uint256","name":"_positionId","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawSwapped","outputs":[{"internalType":"uint256","name":"_swapped","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IDCAHub","name":"_hub","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256[]","name":"positionIds","type":"uint256[]"}],"internalType":"struct IDCAHubPositionHandler.PositionSet[]","name":"_positions","type":"tuple[]"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawSwappedMany","outputs":[{"internalType":"uint256[]","name":"_withdrawn","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a06040523480156200001157600080fd5b506040516200553d3803806200553d8339810160408190526200003491620000ca565b83838383816001600160a01b038116620000615760405163e6250e3360e01b815260040160405180910390fd5b600080546001600160a01b03199081166001600160a01b03938416179091556002805482169683169690961790955560038054909516938116939093179093555016608052506200013292505050565b6001600160a01b0381168114620000c757600080fd5b50565b60008060008060808587031215620000e157600080fd5b8451620000ee81620000b1565b60208601519094506200010181620000b1565b60408601519093506200011481620000b1565b60608601519092506200012781620000b1565b939692955090935050565b6080516153e16200015c6000396000818161048201528181610fc8015261140201526153e16000f3fe60806040526004361061021d5760003560e01c806362b850c71161011d578063bcbef206116100b0578063e3056a341161007f578063effca70511610064578063effca705146105e1578063f235757f14610601578063fa0c95551461062157600080fd5b8063e3056a3414610592578063e43581b8146105b257600080fd5b8063bcbef2061461052a578063d9a673ef1461053d578063db8266de14610550578063df08aed51461056357600080fd5b8063910cab11116100ec578063910cab11146104b7578063a669a7f6146104d7578063ac9650d8146104ea578063bb2871a51461050a57600080fd5b806362b850c71461044a57806362dd9af31461045d5780636afdd8501461047057806389352328146104a457600080fd5b806335ac2a50116101b057806340c5710c1161017f5780635684c275116101645780635684c275146103cd578063585cc6a5146103fa5780635f963dcf1461042257600080fd5b806340c5710c1461038d578063484b3577146103a057600080fd5b806335ac2a501461032757806337b0c09d146103475780633a79d6741461035a5780633ed242b41461036d57600080fd5b80631f66925c116101ec5780631f66925c146102c05780631f8b479d146102e15780632b3297f9146102f4578063340b532f1461031457600080fd5b806301ffc9a71461022957806305ce20d61461025e5780630c340a241461027357806313f6986d146102ab57600080fd5b3661022457005b600080fd5b34801561023557600080fd5b50610249610244366004613395565b610634565b60405190151581526020015b60405180910390f35b61027161026c36600461340e565b6106cd565b005b34801561027f57600080fd5b50600054610293906001600160a01b031681565b6040516001600160a01b039091168152602001610255565b3480156102b757600080fd5b50610271610801565b6102d36102ce366004613463565b6108ad565b604051908152602001610255565b6102716102ef366004613502565b610955565b34801561030057600080fd5b50600254610293906001600160a01b031681565b61027161032236600461358e565b6109a6565b61033a6103353660046135c7565b6109b4565b6040516102559190613624565b610271610355366004613463565b610b03565b610271610368366004613463565b610b1d565b61038061037b366004613668565b610b28565b604051610255919061372f565b61027161039b3660046137b1565b610c00565b3480156103ac57600080fd5b506103c06103bb36600461387d565b610d83565b6040516102559190613914565b3480156103d957600080fd5b506103ed6103e8366004613a45565b610e48565b6040516102559190613a9a565b34801561040657600080fd5b5061029373eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b610435610430366004613ba8565b610f06565b60408051928352602083019190915201610255565b610271610458366004613bf0565b610fbb565b61027161046b366004613c87565b610ffd565b34801561047c57600080fd5b506102937f000000000000000000000000000000000000000000000000000000000000000081565b6102d36104b2366004613d0a565b611087565b3480156104c357600080fd5b50600354610293906001600160a01b031681565b6102d36104e5366004613deb565b611132565b6104fd6104f8366004613668565b61122d565b6040516102559190613e5f565b34801561051657600080fd5b5061033a610525366004613ed4565b611310565b610271610538366004613f31565b611326565b61027161054b366004613f67565b6113f5565b61027161055e366004613fde565b61142e565b34801561056f57600080fd5b5061024961057e36600461402f565b6001546001600160a01b0391821691161490565b34801561059e57600080fd5b50600154610293906001600160a01b031681565b3480156105be57600080fd5b506102496105cd36600461402f565b6000546001600160a01b0391821691161490565b3480156105ed57600080fd5b506102716105fc36600461358e565b61149e565b34801561060d57600080fd5b5061027161061c36600461402f565b611528565b6102d361062f36600461404c565b6115d8565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f3ed242b40000000000000000000000000000000000000000000000000000000014806106c757507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b838360006106dc83838361171f565b6040517f5b6fd01d000000000000000000000000000000000000000000000000000000008152600481018790526000906001600160a01b03891690635b6fd01d9060240161010060405180830381865afa15801561073e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107629190614261565b519050610770818988611827565b6040517fded700a6000000000000000000000000000000000000000000000000000000008152600481018890526024810187905263ffffffff861660448201526001600160a01b0389169063ded700a6906064015b600060405180830381600087803b1580156107df57600080fd5b505af11580156107f3573d6000803e3d6000fd5b505050505050505050505050565b6001546001600160a01b03163314610845576040517f9ba0305d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018054600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b0384161782559091169091556040517fdc57ca23c46d823853915ed5a090ca0ee9db5eb6a46f5c58e1c9158de861fd769190a1565b6000838360026108be83838361171f565b6040517f17621890000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0386811660248301528816906317621890906044016020604051808303816000875af1158015610926573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094a919061431d565b979650505050505050565b6040517f4faa38830000000000000000000000000000000000000000000000000000000081526001600160a01b03891690634faa3883906107c5908a908a908a908a908a908a908a906004016144da565b6109b082826118da565b5050565b606060005b83811015610a695760008585838181106109d5576109d561451b565b90506020028101906109e7919061454a565b6109f5906020810190614588565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509394505050505b8151811015610a5f57610a5788838381518110610a4857610a4861451b565b6020026020010151600261171f565b600101610a29565b50506001016109b9565b506040517f480b37960000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063480b379690610ab3908790879087906004016145f0565b6000604051808303816000875af1158015610ad2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610afa9190810190614708565b95945050505050565b610b186001600160a01b03841633838561199b565b505050565b610b18838383611a17565b60608167ffffffffffffffff811115610b4357610b4361413a565b604051908082528060200260200182016040528015610b9a57816020015b610b87604051806060016040528060001515815260200160608152602001600081525090565b815260200190600190039081610b615790505b50905060005b82811015610bf957610bd4848483818110610bbd57610bbd61451b565b9050602002810190610bcf9190614793565b611a7f565b828281518110610be657610be661451b565b6020908102919091010152600101610ba0565b5092915050565b82826000610c0f83838361171f565b6040517f5b6fd01d000000000000000000000000000000000000000000000000000000008152600481018690526000906001600160a01b03881690635b6fd01d9060240161010060405180830381865afa158015610c71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c959190614261565b516040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610cf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1d919061431d565b9050610d2a828983611827565b6040517fded700a6000000000000000000000000000000000000000000000000000000008152600481018890526024810182905263ffffffff871660448201526001600160a01b0389169063ded700a6906064016107c5565b6040805180820190915260608082526020820152600080610da48888611bff565b6040517f4997cdc300000000000000000000000000000000000000000000000000000000815291935091506001600160a01b038a1690634997cdc390610df690859085908b908b908b906004016148ab565b600060405180830381865afa158015610e13573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e3b91908101906149db565b9998505050505050505050565b6040805180820190915260608082526020820152600080610e698585611bff565b6040517fd2d95b2d00000000000000000000000000000000000000000000000000000000815291935091506001600160a01b0387169063d2d95b2d90610eb59085908590600401614b46565b600060405180830381865afa158015610ed2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610efa9190810190614b6b565b925050505b9392505050565b60008085856003610f1883838361171f565b6040517f72ada4c5000000000000000000000000000000000000000000000000000000008152600481018990526001600160a01b03888116602483015287811660448301528a16906372ada4c59060640160408051808303816000875af1158015610f87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fab9190614caf565b909a909950975050505050505050565b610ff46001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001688888888888888611c60565b50505050505050565b6040517f3dff05870000000000000000000000000000000000000000000000000000000081526001600160a01b03881690633dff05879061104c90899089908990899089908990600401614cd3565b600060405180830381600087803b15801561106657600080fd5b505af115801561107a573d6000803e3d6000fd5b5050505050505050505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009081906001600160a01b038c16906370a0823190602401602060405180830381865afa1580156110e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061110d919061431d565b90506111228c8c8c848d8d8d8d8d8d8d6115d8565b9c9b505050505050505050505050565b60006001600160a01b0386161561115f5760035461115f906001600160a01b038881169116600019611d1a565b600254611177906001600160a01b0316858588611dd8565b6001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611221576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa1580156111f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121c919061431d565b611223565b475b9695505050505050565b60608167ffffffffffffffff8111156112485761124861413a565b60405190808252806020026020018201604052801561127b57816020015b60608152602001906001900390816112665790505b50905060005b82811015610bf9576112eb3085858481811061129f5761129f61451b565b90506020028101906112b19190614793565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611e2a92505050565b8282815181106112fd576112fd61451b565b6020908102919091010152600101611281565b6060610afa85858585611e97565b949350505050565b60005a9050600080306001600160a01b03168585604051611348929190614d90565b600060405180830381855af49150503d8060008114611383576040519150601f19603f3d011682016040523d82523d6000602084013e611388565b606091505b509150915060005a61139a9085614dcf565b905060405180606001604052808415158152602001838152602001828152506040517f493703af0000000000000000000000000000000000000000000000000000000081526004016113ec9190614de2565b60405180910390fd5b610ff46001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001688888888888888611f73565b8484600161143d83838361171f565b6040517ff1accf39000000000000000000000000000000000000000000000000000000008152600481018890526024810187905263ffffffff861660448201526001600160a01b03858116606483015289169063f1accf39906084016107c5565b6000546001600160a01b031633146114e2576040517fe0a8b92000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546001600160a01b039384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560038054929093169116179055565b6000546001600160a01b0316331461156c576040517fe0a8b92000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f56bddfa0cee9697cebddf9acd7f23dc6583663b05e007b877056d05017994def9060200160405180910390a150565b60006115e58b8d8b611827565b81611685576040517f6b29e1bd0000000000000000000000000000000000000000000000000000000081526001600160a01b038d1690636b29e1bd9061163d908e908e908e908e908e908e908e908e90600401614df5565b6020604051808303816000875af115801561165c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611680919061431d565b611122565b6040517fb2b513c10000000000000000000000000000000000000000000000000000000081526001600160a01b038d169063b2b513c1906116dc908e908e908e908e908e908e908e908e908e908e90600401614e54565b6020604051808303816000875af11580156116fb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611122919061431d565b826001600160a01b031663cc7a20496040518163ffffffff1660e01b8152600401602060405180830381865afa15801561175d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117819190614ecb565b6001600160a01b031663823abfd98333846040518463ffffffff1660e01b81526004016117b093929190614ee8565b602060405180830381865afa1580156117cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f19190614f0b565b610b18576040517f5c427cd900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015611890573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118b4919061431d565b9050818110156118d4576118d46001600160a01b03851684600019611d1a565b50505050565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611986576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa15801561195d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611981919061431d565b611988565b475b90508015610b1857610b18838284611a17565b6040516001600160a01b0384811660248301528381166044830152606482018390526118d49186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050611ffc565b6001600160a01b038116611a285750335b7fffffffffffffffffffffffff11111111111111111111111111111111111111126001600160a01b03841601611a6b57610b186001600160a01b03821683612078565b610b186001600160a01b0384168284612141565b611aa5604051806060016040528060001515815260200160608152602001600081525090565b600080306001600160a01b031663bcbef20660e01b8686604051602401611acd929190614f28565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611b389190614f3c565b600060405180830381855af49150503d8060008114611b73576040519150601f19603f3d011682016040523d82523d6000602084013e611b78565b606091505b50915091508115611be5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5754463f2053686f756c642068617665206661696c656421000000000000000060448201526064016113ec565b60048101905080806020019051810190610afa9190614f4e565b606080611c5484846000604051908082528060200260200182016040528015611c4e57816020015b6040805180820190915260008082526020820152815260200190600190039081611c275790505b50612172565b50909590945092505050565b8515611d1057876001600160a01b031663edd9444b60405180606001604052808a8a808060200260200160405190810160405280939291908181526020016000905b82821015611cce57611cbf60408302860136819003810190615024565b81526020019060010190611ca2565b5050505050815260200188815260200187815250611ced8a8a866121f7565b3387876040518663ffffffff1660e01b81526004016107c59594939291906150ac565b5050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052611d9984826122cc565b6118d4576040516001600160a01b03848116602483015260006044830152611dce91869182169063095ea7b3906064016119d0565b6118d48482611ffc565b611e2383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050506001600160a01b0387169190508361236f565b5050505050565b6060600080846001600160a01b031684604051611e479190614f3c565b600060405180830381855af49150503d8060008114611e82576040519150601f19603f3d011682016040523d82523d6000602084013e611e87565b606091505b5091509150610afa858383612417565b60608267ffffffffffffffff811115611eb257611eb261413a565b604051908082528060200260200182016040528015611edb578160200160208202803683370190505b50905060005b83811015611f6a57611f4586868684818110611eff57611eff61451b565b611f15926020604090920201908101915061402f565b878785818110611f2757611f2761451b565b9050604002016020016020810190611f3f919061402f565b8661248c565b828281518110611f5757611f5761451b565b6020908102919091010152600101611ee1565b50949350505050565b6040805160a0810182526001600160a01b0389811660608301908152608083018a9052825260208083018990528284018890528351808501855285831681529081018a905292517f30f28b7a000000000000000000000000000000000000000000000000000000008152908b16926330f28b7a926107c592909190339089908990600401615166565b60006120116001600160a01b03841683612803565b905080516000141580156120365750808060200190518101906120349190614f0b565b155b15610b18576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024016113ec565b804710156120b4576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016113ec565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612101576040519150601f19603f3d011682016040523d82523d6000602084013e612106565b606091505b5050905080610b18576040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516001600160a01b03838116602483015260448201839052610b1891859182169063a9059cbb906064016119d0565b60608060606121d38686808060200260200160405190810160405280939291908181526020016000905b828210156121c8576121b9604083028601368190038101906151df565b8152602001906001019061219c565b505050505085612811565b92506121e0868685612ccd565b91506121ec8484612f5e565b905093509350939050565b60608267ffffffffffffffff8111156122125761221261413a565b60405190808252806020026020018201604052801561225757816020015b60408051808201909152600080825260208201528152602001906001900390816122305790505b50905060005b81518110156122c4576040518060400160405280846001600160a01b031681526020018686848181106122925761229261451b565b905060400201602001358152508282815181106122b1576122b161451b565b602090810291909101015260010161225d565b509392505050565b6000806000846001600160a01b0316846040516122e99190614f3c565b6000604051808303816000865af19150503d8060008114612326576040519150601f19603f3d011682016040523d82523d6000602084013e61232b565b606091505b50915091508180156123555750805115806123555750808060200190518101906123559190614f0b565b8015610afa5750505050506001600160a01b03163b151590565b6060814710156123ad576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016113ec565b600080856001600160a01b031684866040516123c99190614f3c565b60006040518083038185875af1925050503d8060008114612406576040519150601f19603f3d011682016040523d82523d6000602084013e61240b565b606091505b5091509150610efa8683835b60608261242c576124278261305a565b610eff565b815115801561244357506001600160a01b0384163b155b15612485576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016113ec565b5080610eff565b600080600061249b868661309f565b6040517f582cf84b0000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152808316602483015292945090925060009189169063582cf84b90604401602060405180830381865afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190615220565b90507f010000000000000000000000000000000000000000000000000000000000000060005b7fff00000000000000000000000000000000000000000000000000000000000000808316908416108015906125ab57507fff00000000000000000000000000000000000000000000000000000000000000821615155b156127f257817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168284167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916036127c4576040517f808ba8e00000000000000000000000000000000000000000000000000000000081526001600160a01b038a8116600483015289811660248301527fff000000000000000000000000000000000000000000000000000000000000008416604483015260009182918291908e169063808ba8e090606401608060405180830381865afa158015612694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b89190615267565b9350935093505060006126ca866130d0565b90506000816126d981866152bf565b6126e4906001615309565b6126ee9190615326565b63ffffffff1690508b612719576127066003836152bf565b6127169063ffffffff168261534e565b90505b6000857bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16118061276457506000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16115b1561279d5742811161278257859a505050505050505050505061131e565b61278c4282614dcf565b9a505050505050505050505061131e565b428111156127be5785156127b157856127bb565b6127bb4282614dcf565b95505b50505050505b6001827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901b9150612555565b506000199998505050505050505050565b6060610eff8383600061236f565b60606000808351855160026128269190615361565b612830919061534e565b67ffffffffffffffff8111156128485761284861413a565b604051908082528060200260200182016040528015612871578160200160208202803683370190505b50905060005b8551811015612a405760008060005b858110801561289c575082801561289a5750815b155b1561297157821580156128f657508884815181106128bc576128bc61451b565b6020026020010151600001516001600160a01b03168582815181106128e3576128e361451b565b60200260200101516001600160a01b0316145b1561290057600192505b81158015612955575088848151811061291b5761291b61451b565b6020026020010151602001516001600160a01b03168582815181106129425761294261451b565b60200260200101516001600160a01b0316145b1561295f57600191505b8061296981615378565b915050612886565b50816129d4578783815181106129895761298961451b565b6020026020010151600001518486806129a190615378565b9750815181106129b3576129b361451b565b60200260200101906001600160a01b031690816001600160a01b0316815250505b80612a36578783815181106129eb576129eb61451b565b602002602001015160200151848680612a0390615378565b975081518110612a1557612a1561451b565b60200260200101906001600160a01b031690816001600160a01b0316815250505b5050600101612877565b5060005b8451811015612b35576000805b8481108015612a5e575081155b15612ac957868381518110612a7557612a7561451b565b6020026020010151600001516001600160a01b0316848281518110612a9c57612a9c61451b565b60200260200101516001600160a01b031603612ab757600191505b80612ac181615378565b915050612a51565b5080612b2c57858281518110612ae157612ae161451b565b602002602001015160000151838580612af990615378565b965081518110612b0b57612b0b61451b565b60200260200101906001600160a01b031690816001600160a01b0316815250505b50600101612a44565b508167ffffffffffffffff811115612b4f57612b4f61413a565b604051908082528060200260200182016040528015612b78578160200160208202803683370190505b50925060005b82811015612cc4576000828281518110612b9a57612b9a61451b565b6020026020010151905060005b816001600160a01b0316868281518110612bc357612bc361451b565b60200260200101516001600160a01b0316108015612c0d575060006001600160a01b0316868281518110612bf957612bf961451b565b60200260200101516001600160a01b031614155b15612c245780612c1c81615378565b915050612ba7565b825b81811115612c8e5786612c3a600183614dcf565b81518110612c4a57612c4a61451b565b6020026020010151878281518110612c6457612c6461451b565b6001600160a01b039092166020928302919091019091015280612c8681615392565b915050612c26565b5081868281518110612ca257612ca261451b565b6001600160a01b03909216602092830291909101909101525050600101612b7e565b50505092915050565b60608267ffffffffffffffff811115612ce857612ce861413a565b604051908082528060200260200182016040528015612d2d57816020015b6040805180820190915260008082526020820152815260200190600190039081612d065790505b5090506000805b83518160ff161015612f55576000612d4d8260016153a9565b90505b84518160ff161015612f425760005b86811015612f2f57878782818110612d7957612d7961451b565b612d8f926020604090920201908101915061402f565b6001600160a01b0316868460ff1681518110612dad57612dad61451b565b60200260200101516001600160a01b0316148015612e215750878782818110612dd857612dd861451b565b9050604002016020016020810190612df0919061402f565b6001600160a01b0316868360ff1681518110612e0e57612e0e61451b565b60200260200101516001600160a01b0316145b80612ee15750878782818110612e3957612e3961451b565b9050604002016020016020810190612e51919061402f565b6001600160a01b0316868460ff1681518110612e6f57612e6f61451b565b60200260200101516001600160a01b0316148015612ee15750878782818110612e9a57612e9a61451b565b612eb0926020604090920201908101915061402f565b6001600160a01b0316868360ff1681518110612ece57612ece61451b565b60200260200101516001600160a01b0316145b15612f27576040805180820190915260ff8085168252831660208201528585612f0981615378565b965081518110612f1b57612f1b61451b565b60200260200101819052505b600101612d5f565b5080612f3a816153c2565b915050612d50565b5080612f4d816153c2565b915050612d34565b50509392505050565b6060815167ffffffffffffffff811115612f7a57612f7a61413a565b604051908082528060200260200182016040528015612fa3578160200160208202803683370190505b50905060005b8351811015610bf95760005b848281518110612fc757612fc761451b565b6020026020010151600001516001600160a01b0316848281518110612fee57612fee61451b565b60200260200101516001600160a01b031614613016578061300e81615378565b915050612fb5565b8482815181106130285761302861451b565b6020026020010151602001518382815181106130465761304661451b565b602090810291909101015250600101612fa9565b80511561306a5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b600080826001600160a01b0316846001600160a01b0316106130c25782846130c5565b83835b909590945092505050565b60007fff0000000000000000000000000000000000000000000000000000000000000082167f0100000000000000000000000000000000000000000000000000000000000000036131235750603c919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f020000000000000000000000000000000000000000000000000000000000000003613175575061012c919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f0400000000000000000000000000000000000000000000000000000000000000036131c75750610384919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f0800000000000000000000000000000000000000000000000000000000000000036132195750610708919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f10000000000000000000000000000000000000000000000000000000000000000361326b5750610e10919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f2000000000000000000000000000000000000000000000000000000000000000036132bd5750613840919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f400000000000000000000000000000000000000000000000000000000000000003613310575062015180919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f800000000000000000000000000000000000000000000000000000000000000003613363575062093a80919050565b6040517fbf3cad0b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602082840312156133a757600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610eff57600080fd5b6001600160a01b038116811461309c57600080fd5b63ffffffff8116811461309c57600080fd5b8035613409816133ec565b919050565b6000806000806080858703121561342457600080fd5b843561342f816133d7565b93506020850135925060408501359150606085013561344d816133ec565b939692955090935050565b8035613409816133d7565b60008060006060848603121561347857600080fd5b8335613483816133d7565b925060208401359150604084013561349a816133d7565b809150509250925092565b60008083601f8401126134b757600080fd5b50813567ffffffffffffffff8111156134cf57600080fd5b6020830191508360208260051b85010111156134ea57600080fd5b9250929050565b803560ff8116811461340957600080fd5b60008060008060008060008060e0898b03121561351e57600080fd5b8835613529816133d7565b9750602089013567ffffffffffffffff81111561354557600080fd5b6135518b828c016134a5565b909850965050604089013594506060890135935061357160808a016134f1565b925060a0890135915060c089013590509295985092959890939650565b600080604083850312156135a157600080fd5b82356135ac816133d7565b915060208301356135bc816133d7565b809150509250929050565b600080600080606085870312156135dd57600080fd5b84356135e8816133d7565b9350602085013567ffffffffffffffff81111561360457600080fd5b613610878288016134a5565b909450925050604085013561344d816133d7565b6020808252825182820181905260009190848201906040850190845b8181101561365c57835183529284019291840191600101613640565b50909695505050505050565b6000806020838503121561367b57600080fd5b823567ffffffffffffffff81111561369257600080fd5b61369e858286016134a5565b90969095509350505050565b60005b838110156136c55781810151838201526020016136ad565b50506000910152565b600081518084526136e68160208601602086016136aa565b601f01601f19169290920160200192915050565b805115158252600060208201516060602085015261371b60608501826136ce565b604093840151949093019390935250919050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156137a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526137928583516136fa565b94509285019290850190600101613758565b5092979650505050505050565b6000806000606084860312156137c657600080fd5b83356137d1816133d7565b925060208401359150604084013561349a816133ec565b60008083601f8401126137fa57600080fd5b50813567ffffffffffffffff81111561381257600080fd5b6020830191508360208260061b85010111156134ea57600080fd5b801515811461309c57600080fd5b60008083601f84011261384d57600080fd5b50813567ffffffffffffffff81111561386557600080fd5b6020830191508360208285010111156134ea57600080fd5b6000806000806000806080878903121561389657600080fd5b86356138a1816133d7565b9550602087013567ffffffffffffffff808211156138be57600080fd5b6138ca8a838b016137e8565b9097509550604089013591506138df8261382d565b909350606088013590808211156138f557600080fd5b5061390289828a0161383b565b979a9699509497509295939492505050565b60006020808352606080840185516040808588015282825180855260809450608089019150868401935060005b8181101561398e5761397e8386516001600160a01b0381511682526020810151602083015260408101516040830152606081015160608301525050565b9387019391850191600101613941565b505088860151888203601f190160408a0152805180835290870193506000918701905b80831015613a3757845180516001600160a01b03908116845289820151168984015284810151858401528781015188840152868101518784015260a0808201519084015260c0908101517fff000000000000000000000000000000000000000000000000000000000000001690830152938701936001929092019160e0909101906139b1565b509998505050505050505050565b600080600060408486031215613a5a57600080fd5b8335613a65816133d7565b9250602084013567ffffffffffffffff811115613a8157600080fd5b613a8d868287016137e8565b9497909650939450505050565b60006020808352606080840185516040808588015282825180855260809450608089019150868401935060005b81811015613b1457613b048386516001600160a01b0381511682526020810151602083015260408101516040830152606081015160608301525050565b9387019391850191600101613ac7565b505088860151888203601f190160408a0152805180835290870193506000918701905b80831015613a3757845180516001600160a01b039081168452898201511689840152848101518584015287810151888401528601517fff000000000000000000000000000000000000000000000000000000000000001686830152938701936001929092019160a090910190613b37565b60008060008060808587031215613bbe57600080fd5b8435613bc9816133d7565b9350602085013592506040850135613be0816133d7565b9150606085013561344d816133d7565b600080600080600080600060a0888a031215613c0b57600080fd5b873567ffffffffffffffff80821115613c2357600080fd5b613c2f8b838c016137e8565b909950975060208a0135965060408a0135955060608a0135915080821115613c5657600080fd5b50613c638a828b0161383b565b9094509250506080880135613c77816133d7565b8091505092959891949750929550565b600080600080600080600060c0888a031215613ca257600080fd5b8735613cad816133d7565b9650602088013567ffffffffffffffff811115613cc957600080fd5b613cd58a828b016134a5565b90975095505060408801359350613cee606089016134f1565b92506080880135915060a0880135905092959891949750929550565b6000806000806000806000806000806101008b8d031215613d2a57600080fd5b8a35613d35816133d7565b995060208b0135613d45816133d7565b985060408b0135613d55816133d7565b975060608b0135613d65816133ec565b965060808b0135613d75816133ec565b9550613d8360a08c01613458565b945060c08b013567ffffffffffffffff80821115613da057600080fd5b613dac8e838f016134a5565b909650945060e08d0135915080821115613dc557600080fd5b50613dd28d828e0161383b565b915080935050809150509295989b9194979a5092959850565b600080600080600060808688031215613e0357600080fd5b8535613e0e816133d7565b945060208601359350604086013567ffffffffffffffff811115613e3157600080fd5b613e3d8882890161383b565b9094509250506060860135613e51816133d7565b809150509295509295909350565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156137a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613ec28583516136ce565b94509285019290850190600101613e88565b60008060008060608587031215613eea57600080fd5b8435613ef5816133d7565b9350602085013567ffffffffffffffff811115613f1157600080fd5b613f1d878288016137e8565b909450925050604085013561344d8161382d565b60008060208385031215613f4457600080fd5b823567ffffffffffffffff811115613f5b57600080fd5b61369e8582860161383b565b600080600080600080600060c0888a031215613f8257600080fd5b8735613f8d816133d7565b9650602088013595506040880135945060608801359350608088013567ffffffffffffffff811115613fbe57600080fd5b613fca8a828b0161383b565b90945092505060a0880135613c77816133d7565b600080600080600060a08688031215613ff657600080fd5b8535614001816133d7565b94506020860135935060408601359250606086013561401f816133ec565b91506080860135613e51816133d7565b60006020828403121561404157600080fd5b8135610eff816133d7565b60008060008060008060008060008060006101208c8e03121561406e57600080fd5b6140788c356133d7565b8b359a5061408960208d01356133d7565b60208c0135995061409c60408d01613458565b985060608c013597506140b160808d016133fe565b96506140bf60a08d016133fe565b95506140cd60c08d01613458565b945067ffffffffffffffff8060e08e013511156140e957600080fd5b6140f98e60e08f01358f016134a5565b90955093506101008d013581101561411057600080fd5b506141228d6101008e01358e0161383b565b81935080925050509295989b509295989b9093969950565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561418c5761418c61413a565b60405290565b60405160e0810167ffffffffffffffff8111828210171561418c5761418c61413a565b60405160a0810167ffffffffffffffff8111828210171561418c5761418c61413a565b6040516060810167ffffffffffffffff8111828210171561418c5761418c61413a565b604051601f8201601f1916810167ffffffffffffffff811182821017156142245761422461413a565b604052919050565b8051613409816133d7565b8051613409816133ec565b80516effffffffffffffffffffffffffffff8116811461340957600080fd5b600061010080838503121561427557600080fd5b6040519081019067ffffffffffffffff821181831017156142985761429861413a565b81604052835191506142a9826133d7565b8181526142b86020850161422c565b60208201526142c960408501614237565b60408201526142da60608501614237565b6060820152608084015160808201526142f560a08501614237565b60a082015260c084015160c082015261431060e08501614242565b60e0820152949350505050565b60006020828403121561432f57600080fd5b5051919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261436b57600080fd5b830160208101925035905067ffffffffffffffff81111561438b57600080fd5b8060051b36038213156134ea57600080fd5b600481106143d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261440c57600080fd5b90910192915050565b60008383855260208086019550808560051b8301018460005b878110156144cd57601f1985840301895261444982886143d8565b60408085018235614459816133d7565b6001600160a01b0316865261447083880184614336565b8789019390935290829052909150606085019060005b838110156144b85781356004811061449d57600080fd5b6144a7848261439d565b509187019190870190600101614486565b5050998501999350509083019060010161442e565b5090979650505050505050565b60c0815260006144ee60c08301898b614415565b602083019790975250604081019490945260ff929092166060840152608083015260a09091015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261457e57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126145bd57600080fd5b83018035915067ffffffffffffffff8211156145d857600080fd5b6020019150600581901b36038213156134ea57600080fd5b60408082528181018490526000906060808401600587811b860183018986805b8b8110156146c6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08a8503018652614649838e6143d8565b8035614654816133d7565b6001600160a01b03168552602061466d82820183614336565b92508a82880152828b8801527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156146a5578485fd5b91871b918281888c0137509687019694018701939290920191600101614610565b5050506001600160a01b0388166020880152945061131e9350505050565b600067ffffffffffffffff8211156146fe576146fe61413a565b5060051b60200190565b6000602080838503121561471b57600080fd5b825167ffffffffffffffff81111561473257600080fd5b8301601f8101851361474357600080fd5b8051614756614751826146e4565b6141fb565b81815260059190911b8201830190838101908783111561477557600080fd5b928401925b8284101561094a5783518252928401929084019061477a565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126147c857600080fd5b83018035915067ffffffffffffffff8211156147e357600080fd5b6020019150368190038213156134ea57600080fd5b60008151808452602080850194506020840160005b838110156148325781516001600160a01b03168752958201959082019060010161480d565b509495945050505050565b60008151808452602080850194506020840160005b83811015614832578151805160ff908116895290840151168388015260409096019590820190600101614852565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6080815260006148be60808301886147f8565b82810360208401526148d0818861483d565b9050851515604084015282810360608401526148ed818587614880565b98975050505050505050565b6000614907614751846146e4565b8381529050602080820190600785901b84018681111561492657600080fd5b845b818110156149a057608080828a0312156149425760008081fd5b604080519182019167ffffffffffffffff831181841017156149665761496661413a565b918152825191614975836133d7565b9182528285015185830152808301519082015260608083015190820152845292820192608001614928565b505050509392505050565b80517fff000000000000000000000000000000000000000000000000000000000000008116811461340957600080fd5b600060208083850312156149ee57600080fd5b825167ffffffffffffffff80821115614a0657600080fd5b81850191506040808388031215614a1c57600080fd5b614a24614169565b835183811115614a3357600080fd5b8401601f81018913614a4457600080fd5b614a528982518884016148f9565b8252508484015183811115614a6657600080fd5b80850194505087601f850112614a7b57600080fd5b83519250614a8b614751846146e4565b83815260e0938402850186019386820191908a861115614aaa57600080fd5b958701955b85871015614b335780878c031215614ac75760008081fd5b614acf614192565b8751614ada816133d7565b815287890151614ae9816133d7565b818a01528786015186820152606080890151908201526080808901519082015260a0808901519082015260c0614b20818a016149ab565b9082015283529586019591870191614aaf565b5095820195909552979650505050505050565b604081526000614b5960408301856147f8565b8281036020840152610afa818561483d565b60006020808385031215614b7e57600080fd5b825167ffffffffffffffff80821115614b9657600080fd5b81850191506040808388031215614bac57600080fd5b614bb4614169565b835183811115614bc357600080fd5b8401601f81018913614bd457600080fd5b614be28982518884016148f9565b8252508484015183811115614bf657600080fd5b80850194505087601f850112614c0b57600080fd5b83519250614c1b614751846146e4565b83815260a0938402850186019386820191908a861115614c3a57600080fd5b958701955b85871015614b335780878c031215614c575760008081fd5b614c5f6141b5565b8751614c6a816133d7565b815287890151614c79816133d7565b818a01528786015186820152606080890151908201526080614c9c818a016149ab565b9082015283529586019591870191614c3f565b60008060408385031215614cc257600080fd5b505080516020909101519092909150565b60a08082528101869052600060c0600588901b8301810190830189835b8a811015614d67577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40868503018352614d29828d6143d8565b6040813586526020614d3d81840184614336565b93508282890152614d518389018583614415565b9750509485019493909301925050600101614cf0565b5050506020830187905260ff861660408401529050606082019390935260800152949350505050565b8183823760009101908152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156106c7576106c7614da0565b602081526000610eff60208301846136fa565b60006001600160a01b03808b168352808a16602084015288604084015263ffffffff808916606085015280881660808501525080861660a08401525060e060c0830152614e4660e083018486614415565b9a9950505050505050505050565b6001600160a01b038b811682528a81166020830152604082018a905263ffffffff898116606084015288166080830152861660a082015261010060c08201819052600090614ea58382018789614415565b905082810360e0840152614eba818587614880565b9d9c50505050505050505050505050565b600060208284031215614edd57600080fd5b8151610eff816133d7565b8381526001600160a01b03831660208201526060810161131e604083018461439d565b600060208284031215614f1d57600080fd5b8151610eff8161382d565b60208152600061131e602083018486614880565b6000825161457e8184602087016136aa565b60006020808385031215614f6157600080fd5b825167ffffffffffffffff80821115614f7957600080fd5b9084019060608287031215614f8d57600080fd5b614f956141d8565b8251614fa08161382d565b81528284015182811115614fb357600080fd5b8301601f81018813614fc457600080fd5b805183811115614fd657614fd661413a565b614fe886601f19601f840116016141fb565b93508084528886828401011115614ffe57600080fd5b61500d818786018885016136aa565b505092830152604090810151908201529392505050565b60006040828403121561503657600080fd5b61503e614169565b8235615049816133d7565b81526020928301359281019290925250919050565b60008151808452602080850194506020840160005b838110156148325761509987835180516001600160a01b03168252602090810151910152565b6040969096019590820190600101615073565b60808152600060e08201875160606080850152818151808452610100860191506020935060208301925060005b81811015615112576150ff83855180516001600160a01b03168252602090810151910152565b92840192604092909201916001016150d9565b505060208a015160a086015260408a015160c0860152848103602086015261513a818a61505e565b9250505061515360408401876001600160a01b03169052565b82810360608401526148ed818587614880565b600061010061518983895180516001600160a01b03168252602090810151910152565b60208801516040840152604088015160608401526151bd608084018880516001600160a01b03168252602090810151910152565b6001600160a01b03861660c08401528060e08401526148ed8184018587614880565b6000604082840312156151f157600080fd5b6151f9614169565b8235615204816133d7565b81526020830135615214816133d7565b60208201529392505050565b60006020828403121561523257600080fd5b610eff826149ab565b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461340957600080fd5b6000806000806080858703121561527d57600080fd5b8451615288816133ec565b93506152966020860161523b565b925060408501516152a6816133ec565b91506152b46060860161523b565b905092959194509250565b600063ffffffff808416806152fd577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b63ffffffff818116838216019080821115610bf957610bf9614da0565b63ffffffff81811683821602808216919082811461534657615346614da0565b505092915050565b808201808211156106c7576106c7614da0565b80820281158282048414176106c7576106c7614da0565b6000600019820361538b5761538b614da0565b5060010190565b6000816153a1576153a1614da0565b506000190190565b60ff81811683821601908111156106c7576106c7614da0565b600060ff821660ff81036153d8576153d8614da0565b6001019291505056000000000000000000000000ed306e38bb930ec9646ff3d917b2e513a97530b10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec864be26084ba3bbf3caacf8f6961a9263319c4000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3

Deployed Bytecode

0x60806040526004361061021d5760003560e01c806362b850c71161011d578063bcbef206116100b0578063e3056a341161007f578063effca70511610064578063effca705146105e1578063f235757f14610601578063fa0c95551461062157600080fd5b8063e3056a3414610592578063e43581b8146105b257600080fd5b8063bcbef2061461052a578063d9a673ef1461053d578063db8266de14610550578063df08aed51461056357600080fd5b8063910cab11116100ec578063910cab11146104b7578063a669a7f6146104d7578063ac9650d8146104ea578063bb2871a51461050a57600080fd5b806362b850c71461044a57806362dd9af31461045d5780636afdd8501461047057806389352328146104a457600080fd5b806335ac2a50116101b057806340c5710c1161017f5780635684c275116101645780635684c275146103cd578063585cc6a5146103fa5780635f963dcf1461042257600080fd5b806340c5710c1461038d578063484b3577146103a057600080fd5b806335ac2a501461032757806337b0c09d146103475780633a79d6741461035a5780633ed242b41461036d57600080fd5b80631f66925c116101ec5780631f66925c146102c05780631f8b479d146102e15780632b3297f9146102f4578063340b532f1461031457600080fd5b806301ffc9a71461022957806305ce20d61461025e5780630c340a241461027357806313f6986d146102ab57600080fd5b3661022457005b600080fd5b34801561023557600080fd5b50610249610244366004613395565b610634565b60405190151581526020015b60405180910390f35b61027161026c36600461340e565b6106cd565b005b34801561027f57600080fd5b50600054610293906001600160a01b031681565b6040516001600160a01b039091168152602001610255565b3480156102b757600080fd5b50610271610801565b6102d36102ce366004613463565b6108ad565b604051908152602001610255565b6102716102ef366004613502565b610955565b34801561030057600080fd5b50600254610293906001600160a01b031681565b61027161032236600461358e565b6109a6565b61033a6103353660046135c7565b6109b4565b6040516102559190613624565b610271610355366004613463565b610b03565b610271610368366004613463565b610b1d565b61038061037b366004613668565b610b28565b604051610255919061372f565b61027161039b3660046137b1565b610c00565b3480156103ac57600080fd5b506103c06103bb36600461387d565b610d83565b6040516102559190613914565b3480156103d957600080fd5b506103ed6103e8366004613a45565b610e48565b6040516102559190613a9a565b34801561040657600080fd5b5061029373eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b610435610430366004613ba8565b610f06565b60408051928352602083019190915201610255565b610271610458366004613bf0565b610fbb565b61027161046b366004613c87565b610ffd565b34801561047c57600080fd5b506102937f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba381565b6102d36104b2366004613d0a565b611087565b3480156104c357600080fd5b50600354610293906001600160a01b031681565b6102d36104e5366004613deb565b611132565b6104fd6104f8366004613668565b61122d565b6040516102559190613e5f565b34801561051657600080fd5b5061033a610525366004613ed4565b611310565b610271610538366004613f31565b611326565b61027161054b366004613f67565b6113f5565b61027161055e366004613fde565b61142e565b34801561056f57600080fd5b5061024961057e36600461402f565b6001546001600160a01b0391821691161490565b34801561059e57600080fd5b50600154610293906001600160a01b031681565b3480156105be57600080fd5b506102496105cd36600461402f565b6000546001600160a01b0391821691161490565b3480156105ed57600080fd5b506102716105fc36600461358e565b61149e565b34801561060d57600080fd5b5061027161061c36600461402f565b611528565b6102d361062f36600461404c565b6115d8565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f3ed242b40000000000000000000000000000000000000000000000000000000014806106c757507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b838360006106dc83838361171f565b6040517f5b6fd01d000000000000000000000000000000000000000000000000000000008152600481018790526000906001600160a01b03891690635b6fd01d9060240161010060405180830381865afa15801561073e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107629190614261565b519050610770818988611827565b6040517fded700a6000000000000000000000000000000000000000000000000000000008152600481018890526024810187905263ffffffff861660448201526001600160a01b0389169063ded700a6906064015b600060405180830381600087803b1580156107df57600080fd5b505af11580156107f3573d6000803e3d6000fd5b505050505050505050505050565b6001546001600160a01b03163314610845576040517f9ba0305d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018054600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b0384161782559091169091556040517fdc57ca23c46d823853915ed5a090ca0ee9db5eb6a46f5c58e1c9158de861fd769190a1565b6000838360026108be83838361171f565b6040517f17621890000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0386811660248301528816906317621890906044016020604051808303816000875af1158015610926573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094a919061431d565b979650505050505050565b6040517f4faa38830000000000000000000000000000000000000000000000000000000081526001600160a01b03891690634faa3883906107c5908a908a908a908a908a908a908a906004016144da565b6109b082826118da565b5050565b606060005b83811015610a695760008585838181106109d5576109d561451b565b90506020028101906109e7919061454a565b6109f5906020810190614588565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509394505050505b8151811015610a5f57610a5788838381518110610a4857610a4861451b565b6020026020010151600261171f565b600101610a29565b50506001016109b9565b506040517f480b37960000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063480b379690610ab3908790879087906004016145f0565b6000604051808303816000875af1158015610ad2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610afa9190810190614708565b95945050505050565b610b186001600160a01b03841633838561199b565b505050565b610b18838383611a17565b60608167ffffffffffffffff811115610b4357610b4361413a565b604051908082528060200260200182016040528015610b9a57816020015b610b87604051806060016040528060001515815260200160608152602001600081525090565b815260200190600190039081610b615790505b50905060005b82811015610bf957610bd4848483818110610bbd57610bbd61451b565b9050602002810190610bcf9190614793565b611a7f565b828281518110610be657610be661451b565b6020908102919091010152600101610ba0565b5092915050565b82826000610c0f83838361171f565b6040517f5b6fd01d000000000000000000000000000000000000000000000000000000008152600481018690526000906001600160a01b03881690635b6fd01d9060240161010060405180830381865afa158015610c71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c959190614261565b516040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610cf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1d919061431d565b9050610d2a828983611827565b6040517fded700a6000000000000000000000000000000000000000000000000000000008152600481018890526024810182905263ffffffff871660448201526001600160a01b0389169063ded700a6906064016107c5565b6040805180820190915260608082526020820152600080610da48888611bff565b6040517f4997cdc300000000000000000000000000000000000000000000000000000000815291935091506001600160a01b038a1690634997cdc390610df690859085908b908b908b906004016148ab565b600060405180830381865afa158015610e13573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e3b91908101906149db565b9998505050505050505050565b6040805180820190915260608082526020820152600080610e698585611bff565b6040517fd2d95b2d00000000000000000000000000000000000000000000000000000000815291935091506001600160a01b0387169063d2d95b2d90610eb59085908590600401614b46565b600060405180830381865afa158015610ed2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610efa9190810190614b6b565b925050505b9392505050565b60008085856003610f1883838361171f565b6040517f72ada4c5000000000000000000000000000000000000000000000000000000008152600481018990526001600160a01b03888116602483015287811660448301528a16906372ada4c59060640160408051808303816000875af1158015610f87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fab9190614caf565b909a909950975050505050505050565b610ff46001600160a01b037f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31688888888888888611c60565b50505050505050565b6040517f3dff05870000000000000000000000000000000000000000000000000000000081526001600160a01b03881690633dff05879061104c90899089908990899089908990600401614cd3565b600060405180830381600087803b15801561106657600080fd5b505af115801561107a573d6000803e3d6000fd5b5050505050505050505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009081906001600160a01b038c16906370a0823190602401602060405180830381865afa1580156110e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061110d919061431d565b90506111228c8c8c848d8d8d8d8d8d8d6115d8565b9c9b505050505050505050505050565b60006001600160a01b0386161561115f5760035461115f906001600160a01b038881169116600019611d1a565b600254611177906001600160a01b0316858588611dd8565b6001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611221576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa1580156111f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121c919061431d565b611223565b475b9695505050505050565b60608167ffffffffffffffff8111156112485761124861413a565b60405190808252806020026020018201604052801561127b57816020015b60608152602001906001900390816112665790505b50905060005b82811015610bf9576112eb3085858481811061129f5761129f61451b565b90506020028101906112b19190614793565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611e2a92505050565b8282815181106112fd576112fd61451b565b6020908102919091010152600101611281565b6060610afa85858585611e97565b949350505050565b60005a9050600080306001600160a01b03168585604051611348929190614d90565b600060405180830381855af49150503d8060008114611383576040519150601f19603f3d011682016040523d82523d6000602084013e611388565b606091505b509150915060005a61139a9085614dcf565b905060405180606001604052808415158152602001838152602001828152506040517f493703af0000000000000000000000000000000000000000000000000000000081526004016113ec9190614de2565b60405180910390fd5b610ff46001600160a01b037f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31688888888888888611f73565b8484600161143d83838361171f565b6040517ff1accf39000000000000000000000000000000000000000000000000000000008152600481018890526024810187905263ffffffff861660448201526001600160a01b03858116606483015289169063f1accf39906084016107c5565b6000546001600160a01b031633146114e2576040517fe0a8b92000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546001600160a01b039384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560038054929093169116179055565b6000546001600160a01b0316331461156c576040517fe0a8b92000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f56bddfa0cee9697cebddf9acd7f23dc6583663b05e007b877056d05017994def9060200160405180910390a150565b60006115e58b8d8b611827565b81611685576040517f6b29e1bd0000000000000000000000000000000000000000000000000000000081526001600160a01b038d1690636b29e1bd9061163d908e908e908e908e908e908e908e908e90600401614df5565b6020604051808303816000875af115801561165c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611680919061431d565b611122565b6040517fb2b513c10000000000000000000000000000000000000000000000000000000081526001600160a01b038d169063b2b513c1906116dc908e908e908e908e908e908e908e908e908e908e90600401614e54565b6020604051808303816000875af11580156116fb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611122919061431d565b826001600160a01b031663cc7a20496040518163ffffffff1660e01b8152600401602060405180830381865afa15801561175d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117819190614ecb565b6001600160a01b031663823abfd98333846040518463ffffffff1660e01b81526004016117b093929190614ee8565b602060405180830381865afa1580156117cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f19190614f0b565b610b18576040517f5c427cd900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015611890573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118b4919061431d565b9050818110156118d4576118d46001600160a01b03851684600019611d1a565b50505050565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611986576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa15801561195d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611981919061431d565b611988565b475b90508015610b1857610b18838284611a17565b6040516001600160a01b0384811660248301528381166044830152606482018390526118d49186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050611ffc565b6001600160a01b038116611a285750335b7fffffffffffffffffffffffff11111111111111111111111111111111111111126001600160a01b03841601611a6b57610b186001600160a01b03821683612078565b610b186001600160a01b0384168284612141565b611aa5604051806060016040528060001515815260200160608152602001600081525090565b600080306001600160a01b031663bcbef20660e01b8686604051602401611acd929190614f28565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611b389190614f3c565b600060405180830381855af49150503d8060008114611b73576040519150601f19603f3d011682016040523d82523d6000602084013e611b78565b606091505b50915091508115611be5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5754463f2053686f756c642068617665206661696c656421000000000000000060448201526064016113ec565b60048101905080806020019051810190610afa9190614f4e565b606080611c5484846000604051908082528060200260200182016040528015611c4e57816020015b6040805180820190915260008082526020820152815260200190600190039081611c275790505b50612172565b50909590945092505050565b8515611d1057876001600160a01b031663edd9444b60405180606001604052808a8a808060200260200160405190810160405280939291908181526020016000905b82821015611cce57611cbf60408302860136819003810190615024565b81526020019060010190611ca2565b5050505050815260200188815260200187815250611ced8a8a866121f7565b3387876040518663ffffffff1660e01b81526004016107c59594939291906150ac565b5050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052611d9984826122cc565b6118d4576040516001600160a01b03848116602483015260006044830152611dce91869182169063095ea7b3906064016119d0565b6118d48482611ffc565b611e2383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050506001600160a01b0387169190508361236f565b5050505050565b6060600080846001600160a01b031684604051611e479190614f3c565b600060405180830381855af49150503d8060008114611e82576040519150601f19603f3d011682016040523d82523d6000602084013e611e87565b606091505b5091509150610afa858383612417565b60608267ffffffffffffffff811115611eb257611eb261413a565b604051908082528060200260200182016040528015611edb578160200160208202803683370190505b50905060005b83811015611f6a57611f4586868684818110611eff57611eff61451b565b611f15926020604090920201908101915061402f565b878785818110611f2757611f2761451b565b9050604002016020016020810190611f3f919061402f565b8661248c565b828281518110611f5757611f5761451b565b6020908102919091010152600101611ee1565b50949350505050565b6040805160a0810182526001600160a01b0389811660608301908152608083018a9052825260208083018990528284018890528351808501855285831681529081018a905292517f30f28b7a000000000000000000000000000000000000000000000000000000008152908b16926330f28b7a926107c592909190339089908990600401615166565b60006120116001600160a01b03841683612803565b905080516000141580156120365750808060200190518101906120349190614f0b565b155b15610b18576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024016113ec565b804710156120b4576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016113ec565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612101576040519150601f19603f3d011682016040523d82523d6000602084013e612106565b606091505b5050905080610b18576040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516001600160a01b03838116602483015260448201839052610b1891859182169063a9059cbb906064016119d0565b60608060606121d38686808060200260200160405190810160405280939291908181526020016000905b828210156121c8576121b9604083028601368190038101906151df565b8152602001906001019061219c565b505050505085612811565b92506121e0868685612ccd565b91506121ec8484612f5e565b905093509350939050565b60608267ffffffffffffffff8111156122125761221261413a565b60405190808252806020026020018201604052801561225757816020015b60408051808201909152600080825260208201528152602001906001900390816122305790505b50905060005b81518110156122c4576040518060400160405280846001600160a01b031681526020018686848181106122925761229261451b565b905060400201602001358152508282815181106122b1576122b161451b565b602090810291909101015260010161225d565b509392505050565b6000806000846001600160a01b0316846040516122e99190614f3c565b6000604051808303816000865af19150503d8060008114612326576040519150601f19603f3d011682016040523d82523d6000602084013e61232b565b606091505b50915091508180156123555750805115806123555750808060200190518101906123559190614f0b565b8015610afa5750505050506001600160a01b03163b151590565b6060814710156123ad576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016113ec565b600080856001600160a01b031684866040516123c99190614f3c565b60006040518083038185875af1925050503d8060008114612406576040519150601f19603f3d011682016040523d82523d6000602084013e61240b565b606091505b5091509150610efa8683835b60608261242c576124278261305a565b610eff565b815115801561244357506001600160a01b0384163b155b15612485576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016113ec565b5080610eff565b600080600061249b868661309f565b6040517f582cf84b0000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152808316602483015292945090925060009189169063582cf84b90604401602060405180830381865afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190615220565b90507f010000000000000000000000000000000000000000000000000000000000000060005b7fff00000000000000000000000000000000000000000000000000000000000000808316908416108015906125ab57507fff00000000000000000000000000000000000000000000000000000000000000821615155b156127f257817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168284167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916036127c4576040517f808ba8e00000000000000000000000000000000000000000000000000000000081526001600160a01b038a8116600483015289811660248301527fff000000000000000000000000000000000000000000000000000000000000008416604483015260009182918291908e169063808ba8e090606401608060405180830381865afa158015612694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b89190615267565b9350935093505060006126ca866130d0565b90506000816126d981866152bf565b6126e4906001615309565b6126ee9190615326565b63ffffffff1690508b612719576127066003836152bf565b6127169063ffffffff168261534e565b90505b6000857bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16118061276457506000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16115b1561279d5742811161278257859a505050505050505050505061131e565b61278c4282614dcf565b9a505050505050505050505061131e565b428111156127be5785156127b157856127bb565b6127bb4282614dcf565b95505b50505050505b6001827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901b9150612555565b506000199998505050505050505050565b6060610eff8383600061236f565b60606000808351855160026128269190615361565b612830919061534e565b67ffffffffffffffff8111156128485761284861413a565b604051908082528060200260200182016040528015612871578160200160208202803683370190505b50905060005b8551811015612a405760008060005b858110801561289c575082801561289a5750815b155b1561297157821580156128f657508884815181106128bc576128bc61451b565b6020026020010151600001516001600160a01b03168582815181106128e3576128e361451b565b60200260200101516001600160a01b0316145b1561290057600192505b81158015612955575088848151811061291b5761291b61451b565b6020026020010151602001516001600160a01b03168582815181106129425761294261451b565b60200260200101516001600160a01b0316145b1561295f57600191505b8061296981615378565b915050612886565b50816129d4578783815181106129895761298961451b565b6020026020010151600001518486806129a190615378565b9750815181106129b3576129b361451b565b60200260200101906001600160a01b031690816001600160a01b0316815250505b80612a36578783815181106129eb576129eb61451b565b602002602001015160200151848680612a0390615378565b975081518110612a1557612a1561451b565b60200260200101906001600160a01b031690816001600160a01b0316815250505b5050600101612877565b5060005b8451811015612b35576000805b8481108015612a5e575081155b15612ac957868381518110612a7557612a7561451b565b6020026020010151600001516001600160a01b0316848281518110612a9c57612a9c61451b565b60200260200101516001600160a01b031603612ab757600191505b80612ac181615378565b915050612a51565b5080612b2c57858281518110612ae157612ae161451b565b602002602001015160000151838580612af990615378565b965081518110612b0b57612b0b61451b565b60200260200101906001600160a01b031690816001600160a01b0316815250505b50600101612a44565b508167ffffffffffffffff811115612b4f57612b4f61413a565b604051908082528060200260200182016040528015612b78578160200160208202803683370190505b50925060005b82811015612cc4576000828281518110612b9a57612b9a61451b565b6020026020010151905060005b816001600160a01b0316868281518110612bc357612bc361451b565b60200260200101516001600160a01b0316108015612c0d575060006001600160a01b0316868281518110612bf957612bf961451b565b60200260200101516001600160a01b031614155b15612c245780612c1c81615378565b915050612ba7565b825b81811115612c8e5786612c3a600183614dcf565b81518110612c4a57612c4a61451b565b6020026020010151878281518110612c6457612c6461451b565b6001600160a01b039092166020928302919091019091015280612c8681615392565b915050612c26565b5081868281518110612ca257612ca261451b565b6001600160a01b03909216602092830291909101909101525050600101612b7e565b50505092915050565b60608267ffffffffffffffff811115612ce857612ce861413a565b604051908082528060200260200182016040528015612d2d57816020015b6040805180820190915260008082526020820152815260200190600190039081612d065790505b5090506000805b83518160ff161015612f55576000612d4d8260016153a9565b90505b84518160ff161015612f425760005b86811015612f2f57878782818110612d7957612d7961451b565b612d8f926020604090920201908101915061402f565b6001600160a01b0316868460ff1681518110612dad57612dad61451b565b60200260200101516001600160a01b0316148015612e215750878782818110612dd857612dd861451b565b9050604002016020016020810190612df0919061402f565b6001600160a01b0316868360ff1681518110612e0e57612e0e61451b565b60200260200101516001600160a01b0316145b80612ee15750878782818110612e3957612e3961451b565b9050604002016020016020810190612e51919061402f565b6001600160a01b0316868460ff1681518110612e6f57612e6f61451b565b60200260200101516001600160a01b0316148015612ee15750878782818110612e9a57612e9a61451b565b612eb0926020604090920201908101915061402f565b6001600160a01b0316868360ff1681518110612ece57612ece61451b565b60200260200101516001600160a01b0316145b15612f27576040805180820190915260ff8085168252831660208201528585612f0981615378565b965081518110612f1b57612f1b61451b565b60200260200101819052505b600101612d5f565b5080612f3a816153c2565b915050612d50565b5080612f4d816153c2565b915050612d34565b50509392505050565b6060815167ffffffffffffffff811115612f7a57612f7a61413a565b604051908082528060200260200182016040528015612fa3578160200160208202803683370190505b50905060005b8351811015610bf95760005b848281518110612fc757612fc761451b565b6020026020010151600001516001600160a01b0316848281518110612fee57612fee61451b565b60200260200101516001600160a01b031614613016578061300e81615378565b915050612fb5565b8482815181106130285761302861451b565b6020026020010151602001518382815181106130465761304661451b565b602090810291909101015250600101612fa9565b80511561306a5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b600080826001600160a01b0316846001600160a01b0316106130c25782846130c5565b83835b909590945092505050565b60007fff0000000000000000000000000000000000000000000000000000000000000082167f0100000000000000000000000000000000000000000000000000000000000000036131235750603c919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f020000000000000000000000000000000000000000000000000000000000000003613175575061012c919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f0400000000000000000000000000000000000000000000000000000000000000036131c75750610384919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f0800000000000000000000000000000000000000000000000000000000000000036132195750610708919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f10000000000000000000000000000000000000000000000000000000000000000361326b5750610e10919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f2000000000000000000000000000000000000000000000000000000000000000036132bd5750613840919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f400000000000000000000000000000000000000000000000000000000000000003613310575062015180919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f800000000000000000000000000000000000000000000000000000000000000003613363575062093a80919050565b6040517fbf3cad0b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602082840312156133a757600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610eff57600080fd5b6001600160a01b038116811461309c57600080fd5b63ffffffff8116811461309c57600080fd5b8035613409816133ec565b919050565b6000806000806080858703121561342457600080fd5b843561342f816133d7565b93506020850135925060408501359150606085013561344d816133ec565b939692955090935050565b8035613409816133d7565b60008060006060848603121561347857600080fd5b8335613483816133d7565b925060208401359150604084013561349a816133d7565b809150509250925092565b60008083601f8401126134b757600080fd5b50813567ffffffffffffffff8111156134cf57600080fd5b6020830191508360208260051b85010111156134ea57600080fd5b9250929050565b803560ff8116811461340957600080fd5b60008060008060008060008060e0898b03121561351e57600080fd5b8835613529816133d7565b9750602089013567ffffffffffffffff81111561354557600080fd5b6135518b828c016134a5565b909850965050604089013594506060890135935061357160808a016134f1565b925060a0890135915060c089013590509295985092959890939650565b600080604083850312156135a157600080fd5b82356135ac816133d7565b915060208301356135bc816133d7565b809150509250929050565b600080600080606085870312156135dd57600080fd5b84356135e8816133d7565b9350602085013567ffffffffffffffff81111561360457600080fd5b613610878288016134a5565b909450925050604085013561344d816133d7565b6020808252825182820181905260009190848201906040850190845b8181101561365c57835183529284019291840191600101613640565b50909695505050505050565b6000806020838503121561367b57600080fd5b823567ffffffffffffffff81111561369257600080fd5b61369e858286016134a5565b90969095509350505050565b60005b838110156136c55781810151838201526020016136ad565b50506000910152565b600081518084526136e68160208601602086016136aa565b601f01601f19169290920160200192915050565b805115158252600060208201516060602085015261371b60608501826136ce565b604093840151949093019390935250919050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156137a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526137928583516136fa565b94509285019290850190600101613758565b5092979650505050505050565b6000806000606084860312156137c657600080fd5b83356137d1816133d7565b925060208401359150604084013561349a816133ec565b60008083601f8401126137fa57600080fd5b50813567ffffffffffffffff81111561381257600080fd5b6020830191508360208260061b85010111156134ea57600080fd5b801515811461309c57600080fd5b60008083601f84011261384d57600080fd5b50813567ffffffffffffffff81111561386557600080fd5b6020830191508360208285010111156134ea57600080fd5b6000806000806000806080878903121561389657600080fd5b86356138a1816133d7565b9550602087013567ffffffffffffffff808211156138be57600080fd5b6138ca8a838b016137e8565b9097509550604089013591506138df8261382d565b909350606088013590808211156138f557600080fd5b5061390289828a0161383b565b979a9699509497509295939492505050565b60006020808352606080840185516040808588015282825180855260809450608089019150868401935060005b8181101561398e5761397e8386516001600160a01b0381511682526020810151602083015260408101516040830152606081015160608301525050565b9387019391850191600101613941565b505088860151888203601f190160408a0152805180835290870193506000918701905b80831015613a3757845180516001600160a01b03908116845289820151168984015284810151858401528781015188840152868101518784015260a0808201519084015260c0908101517fff000000000000000000000000000000000000000000000000000000000000001690830152938701936001929092019160e0909101906139b1565b509998505050505050505050565b600080600060408486031215613a5a57600080fd5b8335613a65816133d7565b9250602084013567ffffffffffffffff811115613a8157600080fd5b613a8d868287016137e8565b9497909650939450505050565b60006020808352606080840185516040808588015282825180855260809450608089019150868401935060005b81811015613b1457613b048386516001600160a01b0381511682526020810151602083015260408101516040830152606081015160608301525050565b9387019391850191600101613ac7565b505088860151888203601f190160408a0152805180835290870193506000918701905b80831015613a3757845180516001600160a01b039081168452898201511689840152848101518584015287810151888401528601517fff000000000000000000000000000000000000000000000000000000000000001686830152938701936001929092019160a090910190613b37565b60008060008060808587031215613bbe57600080fd5b8435613bc9816133d7565b9350602085013592506040850135613be0816133d7565b9150606085013561344d816133d7565b600080600080600080600060a0888a031215613c0b57600080fd5b873567ffffffffffffffff80821115613c2357600080fd5b613c2f8b838c016137e8565b909950975060208a0135965060408a0135955060608a0135915080821115613c5657600080fd5b50613c638a828b0161383b565b9094509250506080880135613c77816133d7565b8091505092959891949750929550565b600080600080600080600060c0888a031215613ca257600080fd5b8735613cad816133d7565b9650602088013567ffffffffffffffff811115613cc957600080fd5b613cd58a828b016134a5565b90975095505060408801359350613cee606089016134f1565b92506080880135915060a0880135905092959891949750929550565b6000806000806000806000806000806101008b8d031215613d2a57600080fd5b8a35613d35816133d7565b995060208b0135613d45816133d7565b985060408b0135613d55816133d7565b975060608b0135613d65816133ec565b965060808b0135613d75816133ec565b9550613d8360a08c01613458565b945060c08b013567ffffffffffffffff80821115613da057600080fd5b613dac8e838f016134a5565b909650945060e08d0135915080821115613dc557600080fd5b50613dd28d828e0161383b565b915080935050809150509295989b9194979a5092959850565b600080600080600060808688031215613e0357600080fd5b8535613e0e816133d7565b945060208601359350604086013567ffffffffffffffff811115613e3157600080fd5b613e3d8882890161383b565b9094509250506060860135613e51816133d7565b809150509295509295909350565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156137a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613ec28583516136ce565b94509285019290850190600101613e88565b60008060008060608587031215613eea57600080fd5b8435613ef5816133d7565b9350602085013567ffffffffffffffff811115613f1157600080fd5b613f1d878288016137e8565b909450925050604085013561344d8161382d565b60008060208385031215613f4457600080fd5b823567ffffffffffffffff811115613f5b57600080fd5b61369e8582860161383b565b600080600080600080600060c0888a031215613f8257600080fd5b8735613f8d816133d7565b9650602088013595506040880135945060608801359350608088013567ffffffffffffffff811115613fbe57600080fd5b613fca8a828b0161383b565b90945092505060a0880135613c77816133d7565b600080600080600060a08688031215613ff657600080fd5b8535614001816133d7565b94506020860135935060408601359250606086013561401f816133ec565b91506080860135613e51816133d7565b60006020828403121561404157600080fd5b8135610eff816133d7565b60008060008060008060008060008060006101208c8e03121561406e57600080fd5b6140788c356133d7565b8b359a5061408960208d01356133d7565b60208c0135995061409c60408d01613458565b985060608c013597506140b160808d016133fe565b96506140bf60a08d016133fe565b95506140cd60c08d01613458565b945067ffffffffffffffff8060e08e013511156140e957600080fd5b6140f98e60e08f01358f016134a5565b90955093506101008d013581101561411057600080fd5b506141228d6101008e01358e0161383b565b81935080925050509295989b509295989b9093969950565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561418c5761418c61413a565b60405290565b60405160e0810167ffffffffffffffff8111828210171561418c5761418c61413a565b60405160a0810167ffffffffffffffff8111828210171561418c5761418c61413a565b6040516060810167ffffffffffffffff8111828210171561418c5761418c61413a565b604051601f8201601f1916810167ffffffffffffffff811182821017156142245761422461413a565b604052919050565b8051613409816133d7565b8051613409816133ec565b80516effffffffffffffffffffffffffffff8116811461340957600080fd5b600061010080838503121561427557600080fd5b6040519081019067ffffffffffffffff821181831017156142985761429861413a565b81604052835191506142a9826133d7565b8181526142b86020850161422c565b60208201526142c960408501614237565b60408201526142da60608501614237565b6060820152608084015160808201526142f560a08501614237565b60a082015260c084015160c082015261431060e08501614242565b60e0820152949350505050565b60006020828403121561432f57600080fd5b5051919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261436b57600080fd5b830160208101925035905067ffffffffffffffff81111561438b57600080fd5b8060051b36038213156134ea57600080fd5b600481106143d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261440c57600080fd5b90910192915050565b60008383855260208086019550808560051b8301018460005b878110156144cd57601f1985840301895261444982886143d8565b60408085018235614459816133d7565b6001600160a01b0316865261447083880184614336565b8789019390935290829052909150606085019060005b838110156144b85781356004811061449d57600080fd5b6144a7848261439d565b509187019190870190600101614486565b5050998501999350509083019060010161442e565b5090979650505050505050565b60c0815260006144ee60c08301898b614415565b602083019790975250604081019490945260ff929092166060840152608083015260a09091015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261457e57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126145bd57600080fd5b83018035915067ffffffffffffffff8211156145d857600080fd5b6020019150600581901b36038213156134ea57600080fd5b60408082528181018490526000906060808401600587811b860183018986805b8b8110156146c6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08a8503018652614649838e6143d8565b8035614654816133d7565b6001600160a01b03168552602061466d82820183614336565b92508a82880152828b8801527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156146a5578485fd5b91871b918281888c0137509687019694018701939290920191600101614610565b5050506001600160a01b0388166020880152945061131e9350505050565b600067ffffffffffffffff8211156146fe576146fe61413a565b5060051b60200190565b6000602080838503121561471b57600080fd5b825167ffffffffffffffff81111561473257600080fd5b8301601f8101851361474357600080fd5b8051614756614751826146e4565b6141fb565b81815260059190911b8201830190838101908783111561477557600080fd5b928401925b8284101561094a5783518252928401929084019061477a565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126147c857600080fd5b83018035915067ffffffffffffffff8211156147e357600080fd5b6020019150368190038213156134ea57600080fd5b60008151808452602080850194506020840160005b838110156148325781516001600160a01b03168752958201959082019060010161480d565b509495945050505050565b60008151808452602080850194506020840160005b83811015614832578151805160ff908116895290840151168388015260409096019590820190600101614852565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6080815260006148be60808301886147f8565b82810360208401526148d0818861483d565b9050851515604084015282810360608401526148ed818587614880565b98975050505050505050565b6000614907614751846146e4565b8381529050602080820190600785901b84018681111561492657600080fd5b845b818110156149a057608080828a0312156149425760008081fd5b604080519182019167ffffffffffffffff831181841017156149665761496661413a565b918152825191614975836133d7565b9182528285015185830152808301519082015260608083015190820152845292820192608001614928565b505050509392505050565b80517fff000000000000000000000000000000000000000000000000000000000000008116811461340957600080fd5b600060208083850312156149ee57600080fd5b825167ffffffffffffffff80821115614a0657600080fd5b81850191506040808388031215614a1c57600080fd5b614a24614169565b835183811115614a3357600080fd5b8401601f81018913614a4457600080fd5b614a528982518884016148f9565b8252508484015183811115614a6657600080fd5b80850194505087601f850112614a7b57600080fd5b83519250614a8b614751846146e4565b83815260e0938402850186019386820191908a861115614aaa57600080fd5b958701955b85871015614b335780878c031215614ac75760008081fd5b614acf614192565b8751614ada816133d7565b815287890151614ae9816133d7565b818a01528786015186820152606080890151908201526080808901519082015260a0808901519082015260c0614b20818a016149ab565b9082015283529586019591870191614aaf565b5095820195909552979650505050505050565b604081526000614b5960408301856147f8565b8281036020840152610afa818561483d565b60006020808385031215614b7e57600080fd5b825167ffffffffffffffff80821115614b9657600080fd5b81850191506040808388031215614bac57600080fd5b614bb4614169565b835183811115614bc357600080fd5b8401601f81018913614bd457600080fd5b614be28982518884016148f9565b8252508484015183811115614bf657600080fd5b80850194505087601f850112614c0b57600080fd5b83519250614c1b614751846146e4565b83815260a0938402850186019386820191908a861115614c3a57600080fd5b958701955b85871015614b335780878c031215614c575760008081fd5b614c5f6141b5565b8751614c6a816133d7565b815287890151614c79816133d7565b818a01528786015186820152606080890151908201526080614c9c818a016149ab565b9082015283529586019591870191614c3f565b60008060408385031215614cc257600080fd5b505080516020909101519092909150565b60a08082528101869052600060c0600588901b8301810190830189835b8a811015614d67577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40868503018352614d29828d6143d8565b6040813586526020614d3d81840184614336565b93508282890152614d518389018583614415565b9750509485019493909301925050600101614cf0565b5050506020830187905260ff861660408401529050606082019390935260800152949350505050565b8183823760009101908152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156106c7576106c7614da0565b602081526000610eff60208301846136fa565b60006001600160a01b03808b168352808a16602084015288604084015263ffffffff808916606085015280881660808501525080861660a08401525060e060c0830152614e4660e083018486614415565b9a9950505050505050505050565b6001600160a01b038b811682528a81166020830152604082018a905263ffffffff898116606084015288166080830152861660a082015261010060c08201819052600090614ea58382018789614415565b905082810360e0840152614eba818587614880565b9d9c50505050505050505050505050565b600060208284031215614edd57600080fd5b8151610eff816133d7565b8381526001600160a01b03831660208201526060810161131e604083018461439d565b600060208284031215614f1d57600080fd5b8151610eff8161382d565b60208152600061131e602083018486614880565b6000825161457e8184602087016136aa565b60006020808385031215614f6157600080fd5b825167ffffffffffffffff80821115614f7957600080fd5b9084019060608287031215614f8d57600080fd5b614f956141d8565b8251614fa08161382d565b81528284015182811115614fb357600080fd5b8301601f81018813614fc457600080fd5b805183811115614fd657614fd661413a565b614fe886601f19601f840116016141fb565b93508084528886828401011115614ffe57600080fd5b61500d818786018885016136aa565b505092830152604090810151908201529392505050565b60006040828403121561503657600080fd5b61503e614169565b8235615049816133d7565b81526020928301359281019290925250919050565b60008151808452602080850194506020840160005b838110156148325761509987835180516001600160a01b03168252602090810151910152565b6040969096019590820190600101615073565b60808152600060e08201875160606080850152818151808452610100860191506020935060208301925060005b81811015615112576150ff83855180516001600160a01b03168252602090810151910152565b92840192604092909201916001016150d9565b505060208a015160a086015260408a015160c0860152848103602086015261513a818a61505e565b9250505061515360408401876001600160a01b03169052565b82810360608401526148ed818587614880565b600061010061518983895180516001600160a01b03168252602090810151910152565b60208801516040840152604088015160608401526151bd608084018880516001600160a01b03168252602090810151910152565b6001600160a01b03861660c08401528060e08401526148ed8184018587614880565b6000604082840312156151f157600080fd5b6151f9614169565b8235615204816133d7565b81526020830135615214816133d7565b60208201529392505050565b60006020828403121561523257600080fd5b610eff826149ab565b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461340957600080fd5b6000806000806080858703121561527d57600080fd5b8451615288816133ec565b93506152966020860161523b565b925060408501516152a6816133ec565b91506152b46060860161523b565b905092959194509250565b600063ffffffff808416806152fd577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b63ffffffff818116838216019080821115610bf957610bf9614da0565b63ffffffff81811683821602808216919082811461534657615346614da0565b505092915050565b808201808211156106c7576106c7614da0565b80820281158282048414176106c7576106c7614da0565b6000600019820361538b5761538b614da0565b5060010190565b6000816153a1576153a1614da0565b506000190190565b60ff81811683821601908111156106c7576106c7614da0565b600060ff821660ff81036153d8576153d8614da0565b6001019291505056

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

000000000000000000000000ed306e38bb930ec9646ff3d917b2e513a97530b10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec864be26084ba3bbf3caacf8f6961a9263319c4000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3

-----Decoded View---------------
Arg [0] : _swapper (address): 0xED306e38BB930ec9646FF3D917B2e513a97530b1
Arg [1] : _allowanceTarget (address): 0x0000000000000000000000000000000000000000
Arg [2] : _governor (address): 0xEC864BE26084ba3bbF3cAAcF8F6961A9263319C4
Arg [3] : _permit2 (address): 0x000000000022D473030F116dDEE9F6B43aC78BA3

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000ed306e38bb930ec9646ff3d917b2e513a97530b1
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 000000000000000000000000ec864be26084ba3bbf3caacf8f6961a9263319c4
Arg [3] : 000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3


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.